@auto_js/v8 0.0.4 → 0.0.6

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 CHANGED
@@ -1,25 +1,20 @@
1
+ # Locate dependencies
1
2
  find_package(Boost 1.83 REQUIRED)
2
- if(PACKAGE_NAME STREQUAL "@auto_js/v8")
3
- set(TARGET_NAME "v8_js")
4
- find_package_from_npm(@auto_js/v8_exports)
5
- elseif(PACKAGE_NAME STREQUAL "@isolated-vm/embedded_v8_js")
6
- set(TARGET_NAME "embedded_v8_js")
7
- find_package_from_npm(@auto_js/v8_exports @isolated-vm/embedded_v8)
8
- else()
9
- message(FATAL_ERROR "Missing or unknown @auto_js/v8 PACKAGE_NAME: '${PACKAGE_NAME}'")
10
- endif()
11
- find_package_from_npm(@auto_js/js)
3
+ find_package_from_npm(@auto_js/v8_exports AS @auto_js/${AUTO_JS_PREFIX}v8_exports PROPAGATE google_v8)
4
+ find_package_from_npm(@auto_js/js AS @auto_js/${AUTO_JS_PREFIX}js PROPAGATE auto_js)
12
5
 
13
- add_library("${TARGET_NAME}" OBJECT)
14
- target_link_libraries("${TARGET_NAME}" PUBLIC Boost::boost auto_js utility_js)
15
- if(TARGET_NAME STREQUAL "v8_js")
16
- target_link_libraries("${TARGET_NAME}" PUBLIC nodejs_v8)
17
- else()
18
- target_compile_definitions("${TARGET_NAME}" PRIVATE INCLUDE_ISOLATED_V8)
19
- target_link_libraries("${TARGET_NAME}" PUBLIC google_v8)
20
- endif()
6
+ # Initialize target [v8_js or v8_isolated_js]
7
+ set(v8_js ${AUTO_JS_PREFIX}v8_js)
8
+ add_library(${v8_js} STATIC)
9
+ set_npm_package_properties(@auto_js/v8 auto_js v8_js)
10
+ target_compile_features(${auto_js} PUBLIC cxx_std_23)
11
+
12
+ # Link dependencies
13
+ target_link_libraries(${v8_js} PRIVATE Boost::boost ${auto_js} utility_js)
14
+ target_link_libraries(${v8_js} PUBLIC ${google_v8})
21
15
 
22
- target_sources("${TARGET_NAME}"
16
+ # Common sources (nodejs + iv8::isolated)
17
+ target_sources(${v8_js}
23
18
  PRIVATE
24
19
  handle/remote.cc
25
20
  support/error.cc
@@ -49,14 +44,19 @@ target_sources("${TARGET_NAME}"
49
44
  value/array.h.cc
50
45
  value/callback_info.cc
51
46
  value/fixed_array.h.cc
47
+ value/function.cc
48
+ value/function.h.cc
52
49
  value/handle.cc
53
50
  value/object.h.cc
54
51
  value/primitive.h.cc
55
52
  value/tag.cc
53
+ value/value.cc
56
54
  )
57
55
 
58
- if(TARGET_NAME STREQUAL "embedded_v8_js")
59
- target_sources("${TARGET_NAME}"
56
+ # iv8::isolated sources
57
+ if(v8_js STREQUAL isolated_v8_js)
58
+ target_compile_definitions(${v8_js} PRIVATE INCLUDE_ISOLATED_V8)
59
+ target_sources(${v8_js}
60
60
  PRIVATE
61
61
  evaluation/module.cc
62
62
  evaluation/script.cc
package/_module.cc CHANGED
@@ -1,21 +1,18 @@
1
1
  export module v8_js;
2
2
  export import :accept;
3
- export import :array_buffer;
4
- export import :array;
5
3
  export import :boost;
6
4
  export import :callback_info;
7
5
  export import :callback;
8
6
  export import :collected_handle;
9
7
  export import :error;
10
- export import :fixed_array;
8
+ export import :function_definitions;
11
9
  export import :handle;
12
10
  export import :handle.value;
13
11
  export import :hash;
14
12
  export import :lock;
15
- export import :object;
16
- export import :primitive;
17
13
  export import :remote;
18
14
  export import :unmaybe;
15
+ export import :value;
19
16
  export import :value.tag;
20
17
  export import :visit;
21
18
  export import :weak_map;
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@auto_js/v8",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
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.4",
9
- "@auto_js/v8_exports": "^0.0.2"
8
+ "@auto_js/js": "^0.0.6",
9
+ "@auto_js/v8_exports": "^0.0.4"
10
10
  },
11
11
  "repository": {
12
12
  "type": "git",
@@ -35,7 +35,7 @@ constexpr auto make_free_function_with_try_catch =
35
35
  callback,
36
36
  std::tuple_cat(
37
37
  std::forward_as_tuple(lock),
38
- js::transfer_out<std::tuple<Args...>>(info, lock)
38
+ js::transfer_out<std::tuple<js::functional::parameter_transfer_as_t<Args>...>>(info, lock)
39
39
  )
40
40
  );
41
41
  }};
@@ -15,9 +15,7 @@ constexpr auto make_plain_callback =
15
15
  ) -> auto {
16
16
  return util::bind{
17
17
  [](auto& function, const v8::FunctionCallbackInfo<v8::Value>& info) -> void {
18
- auto isolate_witness = isolate_lock_witness::make_witness(info.GetIsolate());
19
- auto context_witness = context_lock_witness::from_isolate(isolate_witness);
20
- function(context_witness, info);
18
+ function(context_lock_witness{info}, info);
21
19
  },
22
20
  std::move(function),
23
21
  };
@@ -32,9 +30,8 @@ constexpr auto make_decorated_callback =
32
30
  ) -> auto {
33
31
  return util::bind{
34
32
  [](auto& agent, auto& function, const v8::FunctionCallbackInfo<v8::Value>& info) -> void {
35
- auto isolate_witness = isolate_lock_witness::make_witness(info.GetIsolate());
36
- auto context_witness = context_lock_witness::from_isolate(isolate_witness);
37
- auto decorated_witness = context_lock_witness_of<Agent, Implements...>{context_witness, agent};
33
+ auto witness = context_lock_witness{info};
34
+ auto decorated_witness = context_lock_witness_of<Agent, Implements...>{witness, agent};
38
35
  function(decorated_witness, info);
39
36
  },
40
37
  std::ref(*lock),
package/support/lock.cc CHANGED
@@ -36,10 +36,6 @@ isolate_unlock::isolate_unlock(isolate_lock_witness lock) :
36
36
  unlocker_{lock.isolate()} {}
37
37
 
38
38
  // `context_lock_witness`
39
- auto context_lock_witness::from_isolate(isolate_lock_witness lock) -> context_lock_witness {
40
- return context_lock_witness{lock, lock.isolate()->GetCurrentContext()};
41
- }
42
-
43
39
  auto context_lock_witness::make_witness(isolate_lock_witness lock, v8::Local<v8::Context> context) -> context_lock_witness {
44
40
  return context_lock_witness{lock, context};
45
41
  }
package/support/lock.h.cc CHANGED
@@ -105,8 +105,11 @@ export class context_lock_witness : public isolate_lock_witness {
105
105
  context_{context} {}
106
106
 
107
107
  public:
108
+ explicit context_lock_witness(const v8::FunctionCallbackInfo<v8::Value>& info) :
109
+ isolate_lock_witness{isolate_lock_witness::make_witness(info.GetIsolate())},
110
+ context_{isolate()->GetCurrentContext()} {}
111
+
108
112
  [[nodiscard]] auto context() const -> v8::Local<v8::Context> { return context_; }
109
- [[nodiscard]] static auto from_isolate(isolate_lock_witness lock) -> context_lock_witness;
110
113
  [[nodiscard]] static auto make_witness(isolate_lock_witness lock, v8::Local<v8::Context> context) -> context_lock_witness;
111
114
 
112
115
  private:
@@ -108,14 +108,14 @@ auto accept_v8_value::operator()(error_tag /*tag*/, visit_holder /*visit*/, cons
108
108
  }
109
109
 
110
110
  // function
111
- auto accept_v8_value::operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, v8::Local<v8::FunctionTemplate> subject) const -> v8::Local<v8::Function> {
112
- return unmaybe(subject->GetFunction(context_));
111
+ auto accept_v8_value::operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, v8::Local<v8::FunctionTemplate> subject) const -> v8::Local<iv8::Function> {
112
+ return unmaybe(subject->GetFunction(context_).As<iv8::Function>());
113
113
  }
114
114
 
115
115
  // data blocks
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
- auto byte_length = subject.size();
118
+ auto byte_length = subject.byte_length();
119
119
  auto backing_store = v8::ArrayBuffer::NewBackingStore(
120
120
  std::move(subject).acquire_ownership().release(),
121
121
  byte_length,
@@ -130,7 +130,7 @@ auto accept_v8_value::operator()(array_buffer_tag /*tag*/, visit_holder /*visit*
130
130
 
131
131
  auto accept_v8_value::operator()(shared_array_buffer_tag /*tag*/, visit_holder /*visit*/, js::shared_array_buffer&& subject) const
132
132
  -> js::referenceable_value<v8::Local<v8::SharedArrayBuffer>> {
133
- auto byte_length = subject.size();
133
+ auto byte_length = subject.byte_length();
134
134
  auto backing_store = [ & ]() -> auto {
135
135
  if (byte_length == 0) {
136
136
  // v8 does not call the deleter if `byte_length` is zero. So the heap-allocated shared_ptr
@@ -1,8 +1,7 @@
1
1
  export module v8_js:accept;
2
- import :array_buffer;
3
2
  import :callback_storage;
4
3
  import :hash;
5
- import :primitive;
4
+ import :value;
6
5
  import auto_js;
7
6
  import std;
8
7
  import util;
@@ -156,7 +155,7 @@ struct accept_v8_value : accept_v8_primitive {
156
155
  }
157
156
 
158
157
  // function
159
- auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, v8::Local<v8::FunctionTemplate> subject) const -> v8::Local<v8::Function>;
158
+ auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, v8::Local<v8::FunctionTemplate> subject) const -> v8::Local<iv8::Function>;
160
159
 
161
160
  // array
162
161
  auto operator()(this const auto& self, list_tag /*tag*/, auto& visit, auto&& subject)
@@ -188,9 +187,9 @@ struct accept_v8_value : accept_v8_primitive {
188
187
  [](v8::Local<v8::Array> array, auto& self, auto& visit, auto /*&&*/ subject) -> void {
189
188
  std::uint32_t ii = 0;
190
189
  auto&& range = util::into_range(std::forward<decltype(subject)>(subject));
191
- for (auto&& subject : util::forward_range(std::forward<decltype(range)>(range))) {
192
- auto element = visit(std::forward<decltype(subject)>(subject), self);
193
- unmaybe(array->Set(self.context_, ii++, element));
190
+ for (auto&& element : util::forward_range(std::forward<decltype(range)>(range))) {
191
+ auto item = visit(std::forward<decltype(element)>(element), self);
192
+ unmaybe(array->Set(self.context_, ii++, item));
194
193
  }
195
194
  },
196
195
  };
@@ -304,10 +303,10 @@ struct accept_v8_value_with<context_lock_witness> : accept_v8_value {
304
303
  using accept_v8_value::witness;
305
304
 
306
305
  // function instantiation
307
- auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, auto subject) const -> v8::Local<v8::Function> {
306
+ auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, auto subject) const -> v8::Local<iv8::Function> {
308
307
  auto [ function, length ] = make_free_function<context_lock_witness>(std::move(subject).callback);
309
308
  auto [ callback, data ] = make_callback_storage(witness(), std::move(function));
310
- return unmaybe(v8::Function::New(witness().context(), callback, data, length, v8::ConstructorBehavior::kThrow));
309
+ return unmaybe(v8::Function::New(witness().context(), callback, data, length, v8::ConstructorBehavior::kThrow).template As<iv8::Function>());
311
310
  }
312
311
  };
313
312
 
@@ -321,7 +320,7 @@ struct accept_v8_value_with : accept_v8_value {
321
320
  using accept_v8_value::operator();
322
321
 
323
322
  // function instantiation
324
- auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, auto subject) const -> v8::Local<v8::Function> {
323
+ auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, auto subject) const -> v8::Local<iv8::Function> {
325
324
  auto [ function, length ] = make_free_function<Lock>(std::move(subject).callback);
326
325
  auto [ callback, data ] = make_callback_storage(lock_.get(), std::move(function));
327
326
  return v8::Function::New(lock_.get().context(), callback, data, length, v8::ConstructorBehavior::kThrow);
@@ -465,7 +464,7 @@ struct accept_property_subject<v8::Local<Type>> : std::type_identity<v8::Local<v
465
464
 
466
465
  // primitives
467
466
  template <class Meta, class Type>
468
- requires std::is_base_of_v<v8::Primitive, Type>
467
+ requires std::is_convertible_v<Type, v8::Primitive>
469
468
  struct accept<Meta, v8::Local<Type>> : iv8::accept_v8_primitive {
470
469
  using accept_v8_primitive::accept_v8_primitive;
471
470
  };
@@ -478,7 +477,7 @@ struct accept<Meta, v8::Local<Type>> : iv8::accept_v8_value_with<typename Meta::
478
477
 
479
478
  // templates
480
479
  template <class Meta, class Type>
481
- requires std::is_base_of_v<v8::Template, Type>
480
+ requires std::is_convertible_v<Type, v8::Template>
482
481
  struct accept<Meta, v8::Local<Type>> : iv8::accept_v8_template<Meta, typename Meta::accept_context_type> {
483
482
  using iv8::accept_v8_template<Meta, typename Meta::accept_context_type>::accept_v8_template;
484
483
  };
package/transfer/visit.cc CHANGED
@@ -3,8 +3,8 @@ import :array_buffer;
3
3
  import :callback_info;
4
4
  import :handle.value;
5
5
  import :hash;
6
- import :object;
7
6
  import :unmaybe;
7
+ import :value;
8
8
  import auto_js;
9
9
  import std;
10
10
  import v8;
@@ -30,14 +30,14 @@ struct visit_property_name {
30
30
  auto value = value_of{witness(), subject.As<tag_to_v8<Tag>>()};
31
31
  return accept(tag, *this, value);
32
32
  };
33
- if (subject->IsNumber()) {
34
- return accept_as(number_tag_of<std::int32_t>{});
35
- } else if (subject->IsString()) {
33
+ if (subject->IsString()) {
36
34
  if (subject.As<v8::String>()->IsOneByte()) {
37
35
  return accept_as(string_tag_of<char>{});
38
36
  } else {
39
37
  return accept_as(string_tag_of<char16_t>{});
40
38
  }
39
+ } else if (subject->IsNumber()) {
40
+ return accept_as(number_tag_of<std::int32_t>{});
41
41
  } else {
42
42
  return accept_as(symbol_tag{});
43
43
  }
@@ -57,27 +57,15 @@ struct visit_v8_key_literal {
57
57
  explicit constexpr visit_v8_key_literal(auto* /*transfer*/) {}
58
58
 
59
59
  auto operator()(const auto& /*anything*/, const auto& accept_or_visit) -> v8::Local<v8::Name> {
60
- const auto make = util::overloaded{
61
- [](v8::Isolate* isolate, std::string_view subject) -> v8::Local<v8::String> {
62
- const auto* data = reinterpret_cast<const std::uint8_t*>(subject.data());
63
- auto size = static_cast<int>(subject.size());
64
- return unmaybe(v8::String::NewFromOneByte(isolate, data, v8::NewStringType::kInternalized, size));
65
- },
66
- [](v8::Isolate* isolate, std::u16string_view subject) -> v8::Local<v8::String> {
67
- const auto* data = reinterpret_cast<const std::uint16_t*>(subject.data());
68
- auto size = static_cast<int>(subject.size());
69
- return unmaybe(v8::String::NewFromTwoByte(isolate, data, v8::NewStringType::kInternalized, size));
70
- },
71
- };
72
60
  if (local_key_.IsEmpty()) {
73
61
  constexpr auto key = util::make_consteval_string_view(Key);
74
- local_key_ = make(accept_or_visit.witness().isolate(), key);
62
+ local_key_ = transfer_in<v8::Local<v8::Name>>(key, accept_or_visit.witness());
75
63
  }
76
64
  return local_key_;
77
65
  }
78
66
 
79
67
  private:
80
- v8::Local<v8::String> local_key_;
68
+ v8::Local<v8::Name> local_key_;
81
69
  };
82
70
 
83
71
  // Implements `Visit`'s non-caching `immediate()` function as a caching visit operation.
@@ -155,14 +143,10 @@ struct visit_flat_value : reference_map_t<Reference, visit_reference_map_type> {
155
143
  // primitives
156
144
  template <class Accept>
157
145
  auto immediate(v8::Local<v8::Primitive> subject, const Accept& accept) -> accept_target_t<Accept> {
158
- if (subject->IsNullOrUndefined()) {
159
- if (subject->IsNull()) {
160
- null_ = subject;
161
- return accept(null_tag{}, *this, subject);
162
- } else {
163
- undefined_ = subject;
164
- return accept(undefined_tag{}, *this, subject);
165
- }
146
+ if (subject->IsUndefined()) {
147
+ return accept(undefined_tag{}, *this, subject);
148
+ } else if (subject->IsNull()) {
149
+ return accept(null_tag{}, *this, subject);
166
150
  } else if (subject->IsNumber()) {
167
151
  return (*this)(subject.As<v8::Number>(), accept);
168
152
  } else if (subject->IsName()) {
@@ -177,7 +161,7 @@ struct visit_flat_value : reference_map_t<Reference, visit_reference_map_type> {
177
161
  }
178
162
 
179
163
  template <class Accept, class Type>
180
- requires std::is_base_of_v<primitive_tag, iv8::v8_to_tag<Type>>
164
+ requires std::is_convertible_v<Type, v8::Primitive>
181
165
  auto immediate(v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
182
166
  return accept_tagged(subject, accept);
183
167
  }
@@ -230,7 +214,7 @@ struct visit_flat_value : reference_map_t<Reference, visit_reference_map_type> {
230
214
  }
231
215
 
232
216
  template <class Accept, class Type>
233
- requires std::is_base_of_v<data_block_tag, iv8::v8_to_tag<Type>>
217
+ requires std::is_convertible_v<iv8::v8_to_tag<Type>, data_block_tag>
234
218
  auto immediate(v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
235
219
  return accept_tagged(subject, accept);
236
220
  }
@@ -241,15 +225,16 @@ struct visit_flat_value : reference_map_t<Reference, visit_reference_map_type> {
241
225
  return accept_tagged(subject, accept);
242
226
  }
243
227
 
244
- // promise (cannot be accepted)
228
+ // promise (maybe could be forwarded)
245
229
  template <class Accept>
246
230
  auto immediate(v8::Local<v8::Promise> subject, const Accept& accept) -> accept_target_t<Accept> {
247
231
  return accept(promise_tag{}, *this, subject);
248
232
  }
249
233
 
250
- // function (cannot be accepted)
251
- template <class Accept>
252
- auto immediate(v8::Local<v8::Function> subject, const Accept& accept) -> accept_target_t<Accept> {
234
+ // function (can be forwarded)
235
+ template <class Type, class Accept>
236
+ requires std::is_convertible_v<Type, v8::Function>
237
+ auto immediate(v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
253
238
  return accept(function_tag{}, *this, subject);
254
239
  }
255
240
 
@@ -259,13 +244,8 @@ struct visit_flat_value : reference_map_t<Reference, visit_reference_map_type> {
259
244
  return accept(iv8::v8_to_tag<Type>{}, *this, value_of{witness(), subject});
260
245
  }
261
246
 
262
- [[nodiscard]] auto is_cached_null(v8::Local<v8::Value> value) const { return value == null_; }
263
- [[nodiscard]] auto is_cached_undefined(v8::Local<v8::Value> value) const { return value == undefined_; }
264
-
265
247
  private:
266
248
  isolate_lock_witness isolate_lock_;
267
- v8::Local<v8::Primitive> null_;
268
- v8::Local<v8::Primitive> undefined_;
269
249
  };
270
250
 
271
251
  // Primary visitor w/ `context_lock_witness`
@@ -280,9 +260,8 @@ struct visit_value : visit_flat_value<Target> {
280
260
  public:
281
261
  friend struct visit_flat_value<Target>;
282
262
  using visit_type = visit_flat_value<Target>;
263
+ using visit_type::accept_tagged;
283
264
  using visit_type::immediate;
284
- using visit_type::is_cached_null;
285
- using visit_type::is_cached_undefined;
286
265
  using visit_type::lookup_or_visit;
287
266
  using visit_type::witness;
288
267
  using visit_type::operator();
@@ -295,27 +274,74 @@ struct visit_value : visit_flat_value<Target> {
295
274
 
296
275
  template <class Accept>
297
276
  auto operator()(v8::Local<v8::Value> subject, const Accept& accept) -> accept_target_t<Accept> {
298
- // Check known address values before the map lookup
299
- if (is_cached_null(subject)) {
300
- return accept(null_tag{}, *this, subject);
301
- } else if (is_cached_undefined(subject)) {
302
- return accept(undefined_tag{}, *this, subject);
303
- }
304
277
 
305
278
  // Check the reference map, and check type
306
279
  return lookup_or_visit(subject, [ & ]() -> accept_target_t<Accept> {
307
- if (subject->IsObject()) {
308
- return immediate(subject.As<v8::Object>(), accept);
309
- } else {
310
- return immediate(subject.As<v8::Primitive>(), accept);
311
- }
280
+ return util::template_traverse(
281
+ accept_tags_of_v<Accept>,
282
+ util::overloaded{
283
+ // Fast paths
284
+ [ & ](undefined_tag /*tag*/, auto next) -> accept_target_t<Accept> {
285
+ return subject->IsUndefined() ? accept(undefined_tag{}, *this, subject.As<iv8::Undefined>()) : next();
286
+ },
287
+ [ & ](null_tag /*tag*/, auto next) -> accept_target_t<Accept> {
288
+ return subject->IsNull() ? accept(null_tag{}, *this, subject.As<iv8::Null>()) : next();
289
+ },
290
+ [ & ](boolean_tag /*tag*/, auto next) -> accept_target_t<Accept> {
291
+ return subject->IsBoolean() ? (*this)(subject.As<v8::Boolean>(), accept) : next();
292
+ },
293
+ [ & ](number_tag /*tag*/, auto next) -> accept_target_t<Accept> {
294
+ return subject->IsNumber() ? (*this)(subject.As<v8::Number>(), accept) : next();
295
+ },
296
+ [ & ](string_tag /*tag*/, auto next) -> accept_target_t<Accept> {
297
+ return subject->IsString() ? immediate(subject.As<v8::String>(), accept) : next();
298
+ },
299
+ [ & ](bigint_tag /*tag*/, auto next) -> accept_target_t<Accept> {
300
+ return subject->IsBigInt() ? immediate(subject.As<v8::BigInt>(), accept) : next();
301
+ },
302
+ [ & ](date_tag /*tag*/, auto next) -> accept_target_t<Accept> {
303
+ return subject->IsDate() ? immediate(subject.As<v8::Date>(), accept) : next();
304
+ },
305
+ [ & ](list_tag /*tag*/, auto next) -> accept_target_t<Accept> {
306
+ return subject->IsArray() ? immediate(subject.As<v8::Array>(), accept) : next();
307
+ },
308
+ [ & ](function_tag /*tag*/, auto next) -> accept_target_t<Accept> {
309
+ return subject->IsFunction() ? immediate(subject.As<iv8::Function>(), accept) : next();
310
+ },
311
+
312
+ // Unknown tag
313
+ [](auto /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
314
+
315
+ // Slow path
316
+ [ & ]() -> accept_target_t<Accept> {
317
+ if (subject->IsObject()) {
318
+ return immediate(subject.As<v8::Object>(), accept);
319
+ } else {
320
+ return immediate(subject.As<v8::Primitive>(), accept);
321
+ }
322
+ },
323
+ }
324
+ );
312
325
  });
313
326
  }
314
327
 
315
328
  template <class Accept>
316
329
  auto operator()(v8::Local<iv8::DataBlock> subject, const Accept& accept) -> accept_target_t<Accept> {
317
330
  return lookup_or_visit(subject, [ & ]() -> accept_target_t<Accept> {
318
- return immediate(subject, accept);
331
+ return util::template_traverse(
332
+ accept_tags_of_v<Accept>,
333
+ util::overloaded{
334
+ // Fast paths
335
+ [ & ](data_block_tag /*tag*/, auto /*next*/) -> accept_target_t<Accept> { return accept_tagged(subject, accept); },
336
+ [ & ](array_buffer_tag /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
337
+ [ & ](shared_array_buffer_tag /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
338
+
339
+ // Slow path
340
+ [ & ]() -> accept_target_t<Accept> {
341
+ return immediate(subject, accept);
342
+ }
343
+ }
344
+ );
319
345
  });
320
346
  }
321
347
 
@@ -343,7 +369,7 @@ struct visit_value : visit_flat_value<Target> {
343
369
  } else if (subject->IsPromise()) {
344
370
  return immediate(subject.As<v8::Promise>(), accept);
345
371
  } else if (subject->IsFunction()) {
346
- return immediate(subject.As<v8::Function>(), accept);
372
+ return immediate(subject.As<iv8::Function>(), accept);
347
373
  } else {
348
374
  auto visit_entry = visit_entry_pair<visit_property_name<visit_value>, visit_value&>{*this};
349
375
  return accept(dictionary_tag{}, visit_entry, value_of{witness(), subject.As<v8::Object>()});
@@ -370,6 +396,8 @@ struct visit_value : visit_flat_value<Target> {
370
396
  return immediate(subject.As<v8::Uint8Array>(), accept);
371
397
  } else if (subject->IsDataView()) {
372
398
  return immediate(subject.As<v8::DataView>(), accept);
399
+ } else if (subject->IsUint8ClampedArray()) {
400
+ return immediate(subject.As<v8::Uint8ClampedArray>(), accept);
373
401
  } else if (subject->IsInt8Array()) {
374
402
  return immediate(subject.As<v8::Int8Array>(), accept);
375
403
  } else if (subject->IsUint16Array()) {
@@ -380,6 +408,8 @@ struct visit_value : visit_flat_value<Target> {
380
408
  return immediate(subject.As<v8::Uint32Array>(), accept);
381
409
  } else if (subject->IsInt32Array()) {
382
410
  return immediate(subject.As<v8::Int32Array>(), accept);
411
+ } else if (subject->IsFloat16Array()) {
412
+ return immediate(subject.As<v8::Float16Array>(), accept);
383
413
  } else if (subject->IsFloat32Array()) {
384
414
  return immediate(subject.As<v8::Float32Array>(), accept);
385
415
  } else if (subject->IsFloat64Array()) {
@@ -388,15 +418,13 @@ struct visit_value : visit_flat_value<Target> {
388
418
  return immediate(subject.As<v8::BigInt64Array>(), accept);
389
419
  } else if (subject->IsBigUint64Array()) {
390
420
  return immediate(subject.As<v8::BigUint64Array>(), accept);
391
- } else if (subject->IsUint8ClampedArray()) {
392
- return immediate(subject.As<v8::Uint8ClampedArray>(), accept);
393
421
  } else {
394
422
  throw js::type_error{u"Received exotic v8 'ArrayBufferView'"};
395
423
  }
396
424
  }
397
425
 
398
426
  template <class Accept, class Type>
399
- requires std::is_base_of_v<array_buffer_view_tag, v8_to_tag<Type>>
427
+ requires std::is_convertible_v<Type, v8::ArrayBufferView>
400
428
  auto immediate(v8::Local<Type> subject, const Accept& accept) -> accept_target_t<Accept> {
401
429
  return accept(v8_to_tag<Type>{}, *this, value_of{witness(), subject});
402
430
  }
@@ -428,7 +456,7 @@ namespace js {
428
456
 
429
457
  // Name visitor (string + symbol)
430
458
  template <class Meta, class Type>
431
- requires std::is_base_of_v<v8::Primitive, Type>
459
+ requires std::is_convertible_v<Type, v8::Primitive>
432
460
  struct visit<Meta, v8::Local<Type>> : iv8::visit_uncached_flat_value_with<Meta> {
433
461
  using iv8::visit_uncached_flat_value_with<Meta>::visit_uncached_flat_value_with;
434
462
  };
@@ -441,7 +469,7 @@ struct visit<Meta, v8::Local<Type>> : iv8::visit_value_with<Meta> {
441
469
 
442
470
  // Template visitor
443
471
  template <class Meta, class Type>
444
- requires std::is_base_of_v<v8::Template, Type>
472
+ requires std::is_convertible_v<Type, v8::Template>
445
473
  struct visit<Meta, v8::Local<Type>> : iv8::visit_template {
446
474
  using iv8::visit_template::visit_template;
447
475
  };
@@ -4,15 +4,6 @@ import std;
4
4
 
5
5
  namespace js::iv8 {
6
6
 
7
- // `value_for_array_buffer`
8
- value_for_array_buffer::operator js::array_buffer() const {
9
- return js::array_buffer{std::span<std::byte>{*this}};
10
- }
11
-
12
- value_for_array_buffer::operator std::span<std::byte>() const {
13
- return std::span<std::byte>{reinterpret_cast<std::byte*>((*this)->Data()), (*this)->ByteLength()};
14
- }
15
-
16
7
  // `value_for_shared_array_buffer`
17
8
  value_for_shared_array_buffer::operator js::shared_array_buffer() const {
18
9
  auto backing_store = (*this)->GetBackingStore();
@@ -21,8 +12,4 @@ value_for_shared_array_buffer::operator js::shared_array_buffer() const {
21
12
  return js::shared_array_buffer{(*this)->ByteLength(), std::move(data_shared_ptr)};
22
13
  }
23
14
 
24
- value_for_shared_array_buffer::operator std::span<std::byte>() const {
25
- return std::span<std::byte>{reinterpret_cast<std::byte*>((*this)->Data()), (*this)->ByteLength()};
26
- }
27
-
28
15
  } // namespace js::iv8
@@ -7,22 +7,32 @@ import v8;
7
7
 
8
8
  namespace js::iv8 {
9
9
 
10
+ // nb: This handles `v8::ArrayBuffer` and also `v8::SharedArrayBuffer`. They aren't related by
11
+ // inheritance but v8 seem to allow it, and even encourage it.
12
+ class value_for_data_block : public handle_without_lock<v8::ArrayBuffer> {
13
+ public:
14
+ using handle_without_lock<v8::ArrayBuffer>::handle_without_lock;
15
+ [[nodiscard]] auto byte_length() const -> std::size_t { return (*this)->ByteLength(); }
16
+ [[nodiscard]] auto data() const -> std::byte* { return static_cast<std::byte*>((*this)->Data()); }
17
+ explicit operator std::span<std::byte>() const { return {data(), byte_length()}; }
18
+ };
19
+
10
20
  class value_for_array_buffer : public handle_without_lock<v8::ArrayBuffer> {
11
21
  public:
12
22
  using handle_without_lock<v8::ArrayBuffer>::handle_without_lock;
23
+ [[nodiscard]] auto byte_length() const -> std::size_t { return (*this)->ByteLength(); }
13
24
  [[nodiscard]] auto data() const -> std::byte* { return static_cast<std::byte*>((*this)->Data()); }
14
- [[nodiscard]] auto size() const -> std::size_t { return (*this)->ByteLength(); }
15
- explicit operator js::array_buffer() const;
16
- explicit operator std::span<std::byte>() const;
25
+ explicit operator js::array_buffer() const { return js::array_buffer{std::span<std::byte>{*this}}; }
26
+ explicit operator std::span<std::byte>() const { return {data(), byte_length()}; }
17
27
  };
18
28
 
19
29
  class value_for_shared_array_buffer : public handle_without_lock<v8::SharedArrayBuffer> {
20
30
  public:
21
31
  using handle_without_lock<v8::SharedArrayBuffer>::handle_without_lock;
32
+ [[nodiscard]] auto byte_length() const -> std::size_t { return (*this)->ByteLength(); }
22
33
  [[nodiscard]] auto data() const -> std::byte* { return static_cast<std::byte*>((*this)->Data()); }
23
- [[nodiscard]] auto size() const -> std::size_t { return (*this)->ByteLength(); }
24
34
  explicit operator js::shared_array_buffer() const;
25
- explicit operator std::span<std::byte>() const;
35
+ explicit operator std::span<std::byte>() const { return {data(), byte_length()}; }
26
36
  };
27
37
 
28
38
  template <class Type>
@@ -0,0 +1,41 @@
1
+ export module v8_js:function_definitions;
2
+ import :accept;
3
+ import :function;
4
+ import :unmaybe;
5
+ import auto_js;
6
+ import std;
7
+ import v8;
8
+
9
+ namespace js::iv8 {
10
+
11
+ template <class Result = std::monostate>
12
+ auto Function::apply(context_lock_witness lock, auto&& args) -> Result {
13
+ auto result = [ & ]() {
14
+ using args_type = std::vector<v8::Local<v8::Value>>;
15
+ auto handle_scope = v8::EscapableHandleScope{lock.isolate()};
16
+ auto undefined = js::transfer_in_strict<v8::Local<v8::Value>>(std::monostate{}, lock);
17
+ auto argv = js::transfer_in_strict<args_type>(std::forward<decltype(args)>(args), lock);
18
+ auto result = unmaybe(v8::Function::Call(lock.context(), undefined, argv.size(), argv.data()));
19
+ return handle_scope.Escape(result);
20
+ }();
21
+ return js::transfer_out<Result>(result, lock);
22
+ }
23
+
24
+ template <class Result = std::monostate>
25
+ auto Function::call(context_lock_witness lock, auto&&... args) -> Result {
26
+ auto result = [ & ]() {
27
+ using args_type = std::array<v8::Local<v8::Value>, sizeof...(args) + 1>;
28
+ auto handle_scope = v8::EscapableHandleScope{lock.isolate()};
29
+ auto argv = js::transfer_in_strict<args_type>(
30
+ std::tuple_cat(
31
+ std::tuple{std::monostate{}},
32
+ std::forward_as_tuple(std::forward<decltype(args)>(args)...)
33
+ ),
34
+ lock
35
+ );
36
+ return handle_scope.Escape(unmaybe(v8::Function::Call(lock.context(), argv[ 0 ], argv.size() - 1, argv.data() + 1)));
37
+ }();
38
+ return js::transfer_out<Result>(result, lock);
39
+ }
40
+
41
+ } // namespace js::iv8
@@ -0,0 +1,16 @@
1
+ export module v8_js:function;
2
+ import :lock;
3
+ import v8;
4
+
5
+ namespace js::iv8 {
6
+
7
+ class Function : public v8::Function {
8
+ public:
9
+ template <class Result = std::monostate>
10
+ auto apply(context_lock_witness lock, auto&& args) -> Result;
11
+
12
+ template <class Result = std::monostate>
13
+ auto call(context_lock_witness lock, auto&&... args) -> Result;
14
+ };
15
+
16
+ } // namespace js::iv8
package/value/object.cc CHANGED
@@ -1,8 +1,5 @@
1
1
  module v8_js;
2
- import :array;
3
- import :handle;
4
- import :lock;
5
- import :primitive;
2
+ import :object;
6
3
  import std;
7
4
  import v8;
8
5
 
@@ -72,15 +69,15 @@ auto value_for_object::has(v8::Local<v8::Primitive> key) const -> bool {
72
69
  return has_property(context(), util::slice(*this), key);
73
70
  }
74
71
 
75
- auto value_for_object::get(v8::Local<v8::Name> key) const -> v8::Local<v8::Value> {
72
+ auto value_for_object::get(v8::Local<v8::Name> key) const -> mapped_type {
76
73
  return get_property(context(), util::slice(*this), key);
77
74
  }
78
75
 
79
- auto value_for_object::get(v8::Local<v8::Number> key) const -> v8::Local<v8::Value> {
76
+ auto value_for_object::get(v8::Local<v8::Number> key) const -> mapped_type {
80
77
  return get_property(context(), util::slice(*this), key);
81
78
  }
82
79
 
83
- auto value_for_object::get(v8::Local<v8::Primitive> key) const -> v8::Local<v8::Value> {
80
+ auto value_for_object::get(v8::Local<v8::Primitive> key) const -> mapped_type {
84
81
  return get_property(context(), util::slice(*this), key);
85
82
  }
86
83
 
package/value/object.h.cc CHANGED
@@ -1,6 +1,5 @@
1
1
  export module v8_js:object;
2
2
  import :array;
3
- import :primitive;
4
3
  import auto_js;
5
4
  import std;
6
5
  import v8;
@@ -33,9 +32,9 @@ class value_for_object : public handle_with_context<v8::Object> {
33
32
  using range_type = std::ranges::transform_view<std::views::all_t<const value_for_array&>, iterator_transform>;
34
33
  using iterator = std::ranges::iterator_t<range_type>;
35
34
 
36
- [[nodiscard]] auto get(v8::Local<v8::Name> key) const -> v8::Local<v8::Value>;
37
- [[nodiscard]] auto get(v8::Local<v8::Number> key) const -> v8::Local<v8::Value>;
38
- [[nodiscard]] auto get(v8::Local<v8::Primitive> key) const -> v8::Local<v8::Value>;
35
+ [[nodiscard]] auto get(v8::Local<v8::Name> key) const -> mapped_type;
36
+ [[nodiscard]] auto get(v8::Local<v8::Number> key) const -> mapped_type;
37
+ [[nodiscard]] auto get(v8::Local<v8::Primitive> key) const -> mapped_type;
39
38
  [[nodiscard]] auto has(v8::Local<v8::Name> key) const -> bool;
40
39
  [[nodiscard]] auto has(v8::Local<v8::Number> key) const -> bool;
41
40
  [[nodiscard]] auto has(v8::Local<v8::Primitive> key) const -> bool;
@@ -1,5 +1,4 @@
1
1
  module v8_js;
2
- import :handle;
3
2
  import :primitive;
4
3
  import std;
5
4
  import v8;
@@ -20,14 +19,6 @@ value_for_number::operator std::int32_t() const {
20
19
  return this->As<v8::Int32>()->Value();
21
20
  }
22
21
 
23
- value_for_number::operator std::int64_t() const {
24
- return this->As<v8::Integer>()->Value();
25
- }
26
-
27
- value_for_number::operator std::uint32_t() const {
28
- return this->As<v8::Uint32>()->Value();
29
- }
30
-
31
22
  // `value_for_bigint`
32
23
  value_for_bigint::operator js::bigint() const {
33
24
  auto bigint = js::bigint{};
@@ -58,18 +49,6 @@ value_for_string::operator std::string() const {
58
49
  return string;
59
50
  }
60
51
 
61
- value_for_string::operator std::u8string() const {
62
- std::u8string string;
63
- string.resize_and_overwrite((*this)->Utf8LengthV2(isolate()), [ this ](char8_t* data, std::size_t length) -> std::size_t {
64
- if (length > 0) {
65
- auto* data_char = reinterpret_cast<char*>(data);
66
- (*this)->WriteUtf8V2(isolate(), data_char, length, v8::String::WriteOptions::NO_NULL_TERMINATION);
67
- }
68
- return length;
69
- });
70
- return string;
71
- }
72
-
73
52
  value_for_string::operator std::u16string() const {
74
53
  std::u16string string;
75
54
  string.resize_and_overwrite((*this)->Length(), [ this ](char16_t* data, std::size_t length) -> std::size_t {
@@ -17,8 +17,6 @@ class value_for_number : public handle_without_lock<v8::Number> {
17
17
  using handle_without_lock<v8::Number>::handle_without_lock;
18
18
  [[nodiscard]] explicit operator double() const;
19
19
  [[nodiscard]] explicit operator std::int32_t() const;
20
- [[nodiscard]] explicit operator std::int64_t() const;
21
- [[nodiscard]] explicit operator std::uint32_t() const;
22
20
  };
23
21
 
24
22
  class value_for_bigint : public handle_without_lock<v8::BigInt> {
@@ -43,7 +41,6 @@ class value_for_string : public handle_with_isolate<v8::String> {
43
41
  public:
44
42
  using handle_with_isolate<v8::String>::handle_with_isolate;
45
43
  [[nodiscard]] explicit operator std::string() const;
46
- [[nodiscard]] explicit operator std::u8string() const;
47
44
  [[nodiscard]] explicit operator std::u16string() const;
48
45
  };
49
46
 
package/value/tag.cc CHANGED
@@ -7,6 +7,7 @@ import v8;
7
7
  namespace js::iv8 {
8
8
 
9
9
  // These helper classes are provided to avoid extra visit inspections on tagged values
10
+ export class Function;
10
11
  export class BigInt64 : public v8::BigInt {};
11
12
  export class BigIntU64 : public v8::BigInt {};
12
13
  export class BigIntWords : public v8::BigInt {};
@@ -19,7 +20,7 @@ export class Undefined : public v8::Primitive {};
19
20
  // `v8::Local<v8::ArrayBuffer>{}->Buffer()` returns a `v8::Local<v8::ArrayBuffer>`, even in the case
20
21
  // it has a `SharedArrayBuffer`. In this case `buffer->IsArrayBuffer()` will return false. This is a
21
22
  // wrapper to denote that there is a data block but we do not know what kind it is.
22
- class DataBlock : public v8::Object {};
23
+ export class DataBlock : public v8::Object {};
23
24
 
24
25
  constexpr auto tag_to_v8_fn = util::overloaded{
25
26
  [](value_tag /*tag*/) -> std::type_identity<v8::Value> { return {}; },
@@ -42,20 +43,26 @@ constexpr auto tag_to_v8_fn = util::overloaded{
42
43
  [](symbol_tag /*tag*/) -> std::type_identity<v8::Symbol> { return {}; },
43
44
 
44
45
  // objects
45
- [](array_buffer_tag /*tag*/) -> std::type_identity<v8::ArrayBuffer> { return {}; },
46
46
  [](date_tag /*tag*/) -> std::type_identity<v8::Date> { return {}; },
47
47
  [](external_tag /*tag*/) -> std::type_identity<v8::External> { return {}; },
48
- [](function_tag /*tag*/) -> std::type_identity<v8::Function> { return {}; },
48
+ [](function_tag /*tag*/) -> std::type_identity<iv8::Function> { return {}; },
49
49
  [](list_tag /*tag*/) -> std::type_identity<v8::Array> { return {}; },
50
50
  [](object_tag /*tag*/) -> std::type_identity<v8::Object> { return {}; },
51
51
  [](promise_tag /*tag*/) -> std::type_identity<v8::Promise> { return {}; },
52
+ [](vector_tag /*tag*/) -> std::type_identity<v8::Array> { return {}; },
53
+
54
+ // data blocks
55
+ [](array_buffer_tag /*tag*/) -> std::type_identity<v8::ArrayBuffer> { return {}; },
56
+ [](data_block_tag /*tag*/) -> std::type_identity<iv8::DataBlock> { return {}; },
52
57
  [](shared_array_buffer_tag /*tag*/) -> std::type_identity<v8::SharedArrayBuffer> { return {}; },
53
58
 
54
59
  // typed arrays
60
+ [](array_buffer_view_tag /*tag*/) -> std::type_identity<v8::ArrayBufferView> { return {}; },
55
61
  [](data_view_tag /*tag*/) -> std::type_identity<v8::DataView> { return {}; },
56
62
  [](typed_array_tag_of<double> /*tag*/) -> std::type_identity<v8::Float64Array> { return {}; },
57
63
  [](typed_array_tag_of<float> /*tag*/) -> std::type_identity<v8::Float32Array> { return {}; },
58
- [](typed_array_tag_of<std::byte> /*tag*/) -> std::type_identity<v8::Uint8ClampedArray> { return {}; },
64
+ [](typed_array_tag_of<js::float16_t> /*tag*/) -> std::type_identity<v8::Float16Array> { return {}; },
65
+ [](typed_array_tag_of<js::uint8_clamped_t> /*tag*/) -> std::type_identity<v8::Uint8ClampedArray> { return {}; },
59
66
  [](typed_array_tag_of<std::int16_t> /*tag*/) -> std::type_identity<v8::Int16Array> { return {}; },
60
67
  [](typed_array_tag_of<std::int32_t> /*tag*/) -> std::type_identity<v8::Int32Array> { return {}; },
61
68
  [](typed_array_tag_of<std::int64_t> /*tag*/) -> std::type_identity<v8::BigInt64Array> { return {}; },
@@ -94,19 +101,25 @@ constexpr auto v8_to_tag_fn = util::overloaded{
94
101
  [](std::type_identity<v8::Uint32> /*type*/) -> number_tag_of<std::uint32_t> { return {}; },
95
102
 
96
103
  // objects
104
+ [](std::type_identity<iv8::Function> /*type*/) -> function_tag { return {}; },
97
105
  [](std::type_identity<v8::Array> /*type*/) -> list_tag { return {}; },
98
- [](std::type_identity<v8::ArrayBuffer> /*type*/) -> array_buffer_tag { return {}; },
99
106
  [](std::type_identity<v8::Date> /*type*/) -> date_tag { return {}; },
100
107
  [](std::type_identity<v8::External> /*type*/) -> external_tag { return {}; },
101
108
  [](std::type_identity<v8::Function> /*type*/) -> function_tag { return {}; },
102
109
  [](std::type_identity<v8::Object> /*type*/) -> object_tag { return {}; },
103
110
  [](std::type_identity<v8::Promise> /*type*/) -> promise_tag { return {}; },
111
+
112
+ // data blocks
113
+ [](std::type_identity<iv8::DataBlock> /*type*/) -> data_block_tag { return {}; },
114
+ [](std::type_identity<v8::ArrayBuffer> /*type*/) -> array_buffer_tag { return {}; },
104
115
  [](std::type_identity<v8::SharedArrayBuffer> /*type*/) -> shared_array_buffer_tag { return {}; },
105
116
 
106
117
  // typed arrays
118
+ [](std::type_identity<v8::ArrayBufferView> /*type*/) -> array_buffer_view_tag { return {}; },
107
119
  [](std::type_identity<v8::BigInt64Array> /*type*/) -> typed_array_tag_of<std::int64_t> { return {}; },
108
120
  [](std::type_identity<v8::BigUint64Array> /*type*/) -> typed_array_tag_of<std::uint64_t> { return {}; },
109
121
  [](std::type_identity<v8::DataView> /*type*/) -> data_view_tag { return {}; },
122
+ [](std::type_identity<v8::Float16Array> /*type*/) -> typed_array_tag_of<js::float16_t> { return {}; },
110
123
  [](std::type_identity<v8::Float32Array> /*type*/) -> typed_array_tag_of<float> { return {}; },
111
124
  [](std::type_identity<v8::Float64Array> /*type*/) -> typed_array_tag_of<double> { return {}; },
112
125
  [](std::type_identity<v8::Int16Array> /*type*/) -> typed_array_tag_of<std::int16_t> { return {}; },
@@ -115,7 +128,7 @@ constexpr auto v8_to_tag_fn = util::overloaded{
115
128
  [](std::type_identity<v8::Uint16Array> /*type*/) -> typed_array_tag_of<std::uint16_t> { return {}; },
116
129
  [](std::type_identity<v8::Uint32Array> /*type*/) -> typed_array_tag_of<std::uint32_t> { return {}; },
117
130
  [](std::type_identity<v8::Uint8Array> /*type*/) -> typed_array_tag_of<std::uint8_t> { return {}; },
118
- [](std::type_identity<v8::Uint8ClampedArray> /*type*/) -> typed_array_tag_of<std::byte> { return {}; },
131
+ [](std::type_identity<v8::Uint8ClampedArray> /*type*/) -> typed_array_tag_of<js::uint8_clamped_t> { return {}; },
119
132
 
120
133
  // templates
121
134
  [](std::type_identity<v8::FunctionTemplate> /*type*/) -> function_prototype_tag { return {}; },
@@ -130,3 +143,9 @@ export template <class Type>
130
143
  using v8_to_tag = decltype(v8_to_tag_fn(type<Type>));
131
144
 
132
145
  } // namespace js::iv8
146
+
147
+ // Specialization for `js::forward`.
148
+ namespace js {
149
+ template <class Type>
150
+ struct forward_tag_for<v8::Local<Type>> : std::type_identity<iv8::v8_to_tag<Type>> {};
151
+ } // namespace js
package/value/value.cc ADDED
@@ -0,0 +1,8 @@
1
+ export module v8_js:value;
2
+ export import :array_buffer;
3
+ export import :array;
4
+ export import :fixed_array;
5
+ export import :function;
6
+ export import :object;
7
+ export import :primitive;
8
+ export import :value.tag;