@auto_js/v8 0.0.9 → 0.0.10

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/_module.cc CHANGED
@@ -24,6 +24,7 @@ export import :evaluation.script;
24
24
  export import :isolated.agent;
25
25
  export import :isolated.clock;
26
26
  export import :isolated.cluster;
27
+ export import :isolated.executor;
27
28
  export import :isolated.memory_policy;
28
29
  export import :isolated.platform;
29
30
  export import :platform.delegate;
@@ -11,8 +11,8 @@ import v8;
11
11
 
12
12
  namespace js::iv8 {
13
13
 
14
- auto module_record::requests(context_lock_witness lock, v8::Local<v8::Module> module) -> std::vector<module_request> {
15
- auto requests_array = fixed_array{lock.context(), module->GetModuleRequests()};
14
+ auto module_record::requests(context_lock_witness lock) -> std::vector<module_request> {
15
+ auto requests_array = fixed_array{lock.context(), GetModuleRequests()};
16
16
  auto requests_view =
17
17
  requests_array |
18
18
  std::views::transform([ & ](v8::Local<v8::Data> value) -> module_request {
@@ -38,7 +38,7 @@ auto module_record::requests(context_lock_witness lock, v8::Local<v8::Module> mo
38
38
  return {std::from_range, std::move(requests_view)};
39
39
  }
40
40
 
41
- auto module_record::compile(context_lock_witness lock, v8::Local<v8::String> source_text, iv8::source_origin origin) -> expected_module_type {
41
+ auto module_record::compile(context_lock_witness lock, v8::Local<v8::String> source_text, iv8::source_origin origin) -> v8::MaybeLocal<module_record> {
42
42
  auto location = origin.location.value_or(source_location{});
43
43
  auto maybe_resource_name = js::transfer_in_strict<v8::MaybeLocal<v8::String>>(std::move(origin).name, lock);
44
44
  v8::Local<v8::String> resource_name{};
@@ -56,9 +56,7 @@ auto module_record::compile(context_lock_witness lock, v8::Local<v8::String> sou
56
56
  true,
57
57
  };
58
58
  v8::ScriptCompiler::Source source{source_text, script_origin};
59
- return unmaybe_one(lock, [ & ] -> v8::MaybeLocal<v8::Module> {
60
- return v8::ScriptCompiler::CompileModule(lock.isolate(), &source);
61
- });
59
+ return v8::ScriptCompiler::CompileModule(lock.isolate(), &source).As<module_record>();
62
60
  }
63
61
 
64
62
  auto module_record::create_synthetic(
@@ -66,12 +64,12 @@ auto module_record::create_synthetic(
66
64
  v8::Local<v8::String> module_name,
67
65
  std::span<const v8::Local<v8::String>> export_names,
68
66
  std::span<const v8::Local<v8::Data>> export_values
69
- ) -> v8::Local<v8::Module> {
67
+ ) -> v8::Local<module_record> {
70
68
  thread_local std::span<const v8::Local<v8::String>>* tl_export_names;
71
69
  thread_local std::span<const v8::Local<v8::Data>>* tl_export_values;
72
70
  tl_export_names = &export_names;
73
71
  tl_export_values = &export_values;
74
- auto scope_exit = util::scope_exit{[ & ]() {
72
+ auto scope_exit = util::scope_exit{[ & ] {
75
73
  tl_export_names = nullptr;
76
74
  tl_export_values = nullptr;
77
75
  }};
@@ -79,7 +77,7 @@ auto module_record::create_synthetic(
79
77
  // Make `v8::Module` record with immediate evaluation steps
80
78
  v8::Module::SyntheticModuleEvaluationSteps evaluation_steps =
81
79
  [](v8::Local<v8::Context> context, v8::Local<v8::Module> module) -> v8::MaybeLocal<v8::Value> {
82
- auto lock = [ & ]() -> context_lock_witness {
80
+ auto lock = [ & ] -> context_lock_witness {
83
81
  auto isolate_witness = isolate_lock_witness::make_witness(v8::Isolate::GetCurrent());
84
82
  return context_lock_witness::make_witness(isolate_witness, context);
85
83
  }();
@@ -102,19 +100,19 @@ auto module_record::create_synthetic(
102
100
  return resolver->GetPromise();
103
101
  };
104
102
  auto v8_export_names = v8::MemorySpan<const v8::Local<v8::String>>{export_names.begin(), export_names.end()};
105
- auto module_record = v8::Module::CreateSyntheticModule(lock.isolate(), module_name, v8_export_names, evaluation_steps);
103
+ auto synthetic_module = v8::Module::CreateSyntheticModule(lock.isolate(), module_name, v8_export_names, evaluation_steps);
106
104
 
107
105
  // "Link" the module, which is a no-op
108
106
  // auto null_callback = v8::Module::ResolveModuleByIndexCallback{nullptr};
109
107
  auto null_callback = v8::Module::ResolveModuleCallback{nullptr};
110
- unmaybe(module_record->InstantiateModule(lock.context(), null_callback));
108
+ unmaybe(synthetic_module->InstantiateModule(lock.context(), null_callback));
111
109
 
112
110
  // `Evaluate` invokes `evaluation_steps` above
113
- unmaybe(module_record->Evaluate(lock.context()));
114
- return module_record;
111
+ unmaybe(synthetic_module->Evaluate(lock.context()));
112
+ return synthetic_module.As<module_record>();
115
113
  }
116
114
 
117
- auto module_record::link(context_lock_witness lock, v8::Local<v8::Module> module, module_link_record link_record) -> void {
115
+ auto module_record::link(context_lock_witness lock, module_link_record link_record) -> void {
118
116
  // Initialize link state
119
117
  auto module_specifier_map = std::unordered_map<v8::Local<v8::Module>, unsigned, address_hash>{};
120
118
  auto module_id = 0U;
@@ -146,11 +144,13 @@ auto module_record::link(context_lock_witness lock, v8::Local<v8::Module> module
146
144
  return (*linker_ptr)(referrer, module_request_index);
147
145
  }
148
146
  };
149
- unmaybe(module->InstantiateModule(lock.context(), v8_callback));
147
+ // nb: It is strange that we need js here.
148
+ auto allow_js = v8::Isolate::AllowJavascriptExecutionScope{lock.isolate()};
149
+ unmaybe(InstantiateModule(lock.context(), v8_callback));
150
150
  linker_ptr = nullptr;
151
151
  }
152
152
 
153
- auto module_record::link(context_lock_witness lock, v8::Local<v8::Module> module) -> void {
153
+ auto module_record::link(context_lock_witness lock) -> void {
154
154
  // Probably a synthetic module
155
155
  auto v8_callback = v8::Module::ResolveModuleByIndexCallback{
156
156
  [](
@@ -161,12 +161,15 @@ auto module_record::link(context_lock_witness lock, v8::Local<v8::Module> module
161
161
  std::terminate();
162
162
  }
163
163
  };
164
- unmaybe(module->InstantiateModule(lock.context(), v8_callback));
164
+ unmaybe(InstantiateModule(lock.context(), v8_callback));
165
165
  }
166
166
 
167
- auto module_record::evaluate(context_lock_witness lock, v8::Local<v8::Module> module) -> expected_value_type {
168
- auto promise = unmaybe(module->Evaluate(lock.context())).As<v8::Promise>();
169
- if (module->IsGraphAsync()) {
167
+ auto module_record::evaluate(context_lock_witness lock) -> expected_value_type {
168
+ auto promise = [ & ] -> auto {
169
+ auto allow_js = v8::Isolate::AllowJavascriptExecutionScope{lock.isolate()};
170
+ return unmaybe(Evaluate(lock.context())).As<v8::Promise>();
171
+ }();
172
+ if (IsGraphAsync()) {
170
173
  throw std::runtime_error{"Module is async"};
171
174
  }
172
175
  if (promise->State() == v8::Promise::kRejected) {
@@ -45,53 +45,52 @@ export struct module_link_record {
45
45
  };
46
46
 
47
47
  // `v8::Module` utilities
48
- export class module_record {
48
+ export class module_record : public v8::Module {
49
49
  public:
50
- using expected_module_type = std::expected<v8::Local<v8::Module>, js::error_value>;
51
50
  using expected_value_type = std::expected<std::monostate, js::error_value>;
52
51
 
53
- static auto evaluate(context_lock_witness lock, v8::Local<v8::Module> module) -> expected_value_type;
54
- static auto link(context_lock_witness lock, v8::Local<v8::Module> module, module_link_record link_record) -> void;
55
- static auto link(context_lock_witness lock, v8::Local<v8::Module> module) -> void;
56
- static auto requests(context_lock_witness lock, v8::Local<v8::Module> module) -> std::vector<module_request>;
52
+ auto evaluate(context_lock_witness lock) -> expected_value_type;
53
+ auto link(context_lock_witness lock, module_link_record link_record) -> void;
54
+ auto link(context_lock_witness lock) -> void;
55
+ auto requests(context_lock_witness lock) -> std::vector<module_request>;
57
56
 
58
- static auto compile(context_lock_witness lock, auto source_text, iv8::source_origin origin) -> expected_module_type;
57
+ static auto compile(context_lock_witness lock, auto source_text, iv8::source_origin origin) -> v8::MaybeLocal<module_record>;
59
58
 
60
59
  template <class... Types>
61
- static auto create_synthetic(context_lock_witness lock, auto origin, std::tuple<std::in_place_t, Types...> module_interface) -> v8::Local<v8::Module>;
60
+ static auto create_synthetic(context_lock_witness lock, auto origin, std::tuple<std::in_place_t, Types...> module_interface) -> v8::Local<module_record>;
62
61
 
63
62
  template <class... Types>
64
- static auto create_synthetic(context_lock_witness lock, auto origin, std::tuple<Types...> module_interface) -> v8::Local<v8::Module>;
63
+ static auto create_synthetic(context_lock_witness lock, auto origin, std::tuple<Types...> module_interface) -> v8::Local<module_record>;
65
64
 
66
65
  template <class Type>
67
- static auto create_synthetic(context_lock_witness lock, auto origin, std::vector<Type> module_interface) -> v8::Local<v8::Module>;
66
+ static auto create_synthetic(context_lock_witness lock, auto origin, std::vector<Type> module_interface) -> v8::Local<module_record>;
68
67
 
69
68
  static auto create_synthetic(
70
69
  context_lock_witness lock,
71
70
  v8::Local<v8::String> module_name,
72
71
  std::span<const v8::Local<v8::String>> export_names,
73
72
  std::span<const v8::Local<v8::Data>> export_values
74
- ) -> v8::Local<v8::Module>;
73
+ ) -> v8::Local<module_record>;
75
74
 
76
75
  private:
77
- static auto compile(context_lock_witness lock, v8::Local<v8::String> source_text, iv8::source_origin origin) -> expected_module_type;
76
+ static auto compile(context_lock_witness lock, v8::Local<v8::String> source_text, iv8::source_origin origin) -> v8::MaybeLocal<module_record>;
78
77
  };
79
78
 
80
79
  // ---
81
80
 
82
- auto module_record::compile(context_lock_witness lock, auto source_text, iv8::source_origin origin) -> expected_module_type {
81
+ auto module_record::compile(context_lock_witness lock, auto source_text, iv8::source_origin origin) -> v8::MaybeLocal<module_record> {
83
82
  auto local_source_text = js::transfer_in_strict<v8::Local<v8::String>>(std::move(source_text), lock);
84
83
  return compile(lock, local_source_text, std::move(origin));
85
84
  }
86
85
 
87
86
  template <class... Types>
88
- auto module_record::create_synthetic(context_lock_witness lock, auto origin, std::tuple<std::in_place_t, Types...> module_interface) -> v8::Local<v8::Module> {
89
- auto [... ii ] = util::sequence<sizeof...(Types)>;
87
+ auto module_record::create_synthetic(context_lock_witness lock, auto origin, std::tuple<std::in_place_t, Types...> module_interface) -> v8::Local<module_record> {
88
+ constexpr auto [... ii ] = util::sequence<sizeof...(Types)>;
90
89
  return create_synthetic(lock, origin, std::tuple{std::get<ii + 1>(module_interface)...});
91
90
  }
92
91
 
93
92
  template <class... Types>
94
- auto module_record::create_synthetic(context_lock_witness lock, auto origin, std::tuple<Types...> module_interface) -> v8::Local<v8::Module> {
93
+ auto module_record::create_synthetic(context_lock_witness lock, auto origin, std::tuple<Types...> module_interface) -> v8::Local<module_record> {
95
94
  const auto& [... entries ] = module_interface;
96
95
  auto origin_local = js::transfer_in_strict<v8::Local<v8::String>>(std::move(origin), lock);
97
96
  auto names = js::transfer_in_strict<std::array<v8::Local<v8::String>, sizeof...(Types)>>(std::tuple{std::get<0>(entries)...}, lock);
@@ -100,7 +99,7 @@ auto module_record::create_synthetic(context_lock_witness lock, auto origin, std
100
99
  }
101
100
 
102
101
  template <class Type>
103
- auto module_record::create_synthetic(context_lock_witness lock, auto origin, std::vector<Type> module_interface) -> v8::Local<v8::Module> {
102
+ auto module_record::create_synthetic(context_lock_witness lock, auto origin, std::vector<Type> module_interface) -> v8::Local<module_record> {
104
103
  auto origin_local = js::transfer_in_strict<v8::Local<v8::String>>(std::move(origin), lock);
105
104
  auto name_locals = std::vector<v8::Local<v8::String>>{};
106
105
  name_locals.reserve(module_interface.size());
@@ -7,7 +7,7 @@ import v8;
7
7
 
8
8
  namespace js::iv8 {
9
9
 
10
- auto script::compile(context_lock_witness lock, v8::Local<v8::String> code_string, source_origin source_origin) -> expected_script_type {
10
+ auto script::compile(context_lock_witness lock, v8::Local<v8::String> code_string, source_origin source_origin) -> v8::MaybeLocal<script> {
11
11
  auto location = source_origin.location.value_or(source_location{});
12
12
  auto maybe_resource_name = js::transfer_in_strict<v8::MaybeLocal<v8::String>>(std::move(source_origin).name, lock);
13
13
  v8::Local<v8::String> resource_name{};
@@ -15,14 +15,14 @@ auto script::compile(context_lock_witness lock, v8::Local<v8::String> code_strin
15
15
  std::ignore = maybe_resource_name.ToLocal(&resource_name);
16
16
  v8::ScriptOrigin origin{resource_name, location.line, location.column};
17
17
  v8::ScriptCompiler::Source source{code_string, origin};
18
- return unmaybe_one(lock, [ & ] -> v8::MaybeLocal<v8::UnboundScript> {
19
- return v8::ScriptCompiler::CompileUnboundScript(lock.isolate(), &source);
20
- });
18
+ auto unbound_script = v8::ScriptCompiler::CompileUnboundScript(lock.isolate(), &source);
19
+ // nb: `As<script>()` doesn't work because `v8::UnboundScript` doesn't provide a `Cast` function
20
+ return std::bit_cast<v8::MaybeLocal<script>>(unbound_script);
21
21
  }
22
22
 
23
- auto script::run(context_lock_witness lock, v8::Local<v8::UnboundScript> script) -> v8::Local<v8::Value> {
24
- auto maybe_result = script->BindToCurrentContext()->Run(lock.context());
25
- return unmaybe(maybe_result);
23
+ auto script::run(context_lock_witness lock) -> v8::Local<v8::Value> {
24
+ auto allow_js = v8::Isolate::AllowJavascriptExecutionScope{lock.isolate()};
25
+ return unmaybe(BindToCurrentContext()->Run(lock.context()));
26
26
  }
27
27
 
28
28
  } // namespace js::iv8
@@ -7,23 +7,20 @@ import v8;
7
7
 
8
8
  namespace js::iv8 {
9
9
 
10
- export struct script {
10
+ export class script : public v8::UnboundScript {
11
11
  public:
12
- using expected_script_type = std::expected<v8::Local<v8::UnboundScript>, js::error_value>;
13
- using expected_value_type = std::optional<std::expected<js::value_t, js::error_value>>;
14
-
15
12
  // nb: It is undocumented (and even mentions "context independent"), but the script compiler
16
13
  // actually needs a context because it can throw an error and *that* would need a context.
17
- static auto compile(context_lock_witness lock, auto code_string, source_origin source_origin) -> expected_script_type;
18
- static auto run(context_lock_witness lock, v8::Local<v8::UnboundScript> script) -> v8::Local<v8::Value>;
14
+ static auto compile(context_lock_witness lock, auto code_string, source_origin source_origin) -> v8::MaybeLocal<script>;
15
+ auto run(context_lock_witness lock) -> v8::Local<v8::Value>;
19
16
 
20
17
  private:
21
- static auto compile(context_lock_witness lock, v8::Local<v8::String> code_string, source_origin source_origin) -> expected_script_type;
18
+ static auto compile(context_lock_witness lock, v8::Local<v8::String> code_string, source_origin source_origin) -> v8::MaybeLocal<script>;
22
19
  };
23
20
 
24
21
  // ---
25
22
 
26
- auto script::compile(context_lock_witness lock, auto code_string, source_origin source_origin) -> expected_script_type {
23
+ auto script::compile(context_lock_witness lock, auto code_string, source_origin source_origin) -> v8::MaybeLocal<script> {
27
24
  auto local_code_string = js::transfer_in_strict<v8::Local<v8::String>>(std::move(code_string), lock);
28
25
  return compile(lock, local_code_string, std::move(source_origin));
29
26
  }
@@ -19,7 +19,7 @@ class agent_host_of;
19
19
 
20
20
  // `agent_host` behavior parameters
21
21
  export struct behavior_params {
22
- memory_policy::covariant memory_policy;
22
+ isolated::memory_policy::covariant memory_policy;
23
23
  clock::any_clock clock;
24
24
  std::optional<double> random_seed;
25
25
  };
@@ -201,6 +201,9 @@ auto agent_handle_of<Type>::schedule(Task task, Args... args) const -> void {
201
201
  [](std::stop_token /*stop_token*/, const auto& host, const auto& task, auto&&... args) -> void {
202
202
  auto isolate_lock = isolate_execution_lock{host->executor().isolate()};
203
203
  std::visit([](auto& clock) -> void { clock.begin_tick(); }, host->clock());
204
+ // By default we disallow code execution except when explicitly annotated
205
+ auto disallow_js =
206
+ v8::Isolate::DisallowJavascriptExecutionScope{isolate_lock.isolate(), v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE};
204
207
  task(lock{isolate_lock, static_cast<agent_host_of<Type>&>(*host)}, std::forward<decltype(args)>(args)...);
205
208
  },
206
209
  std::move(host),
@@ -42,7 +42,7 @@ auto cluster::make_agent(auto make_environment, behavior_params params, auto cal
42
42
  ) -> auto {
43
43
  auto host = std::make_shared<agent_host_of<environment_type>>(std::move(storage), params);
44
44
  auto isolate_lock = isolate_execution_lock{host->executor().isolate()};
45
- host->emplace_environment(util::elide{[ & ]() -> environment_type {
45
+ host->emplace_environment(util::elide{[ & ] -> environment_type {
46
46
  return make_environment(agent_lock{isolate_lock, *host});
47
47
  }});
48
48
  auto lock = agent_lock_of{isolate_lock, *host};
@@ -42,8 +42,8 @@ auto memory_policy::limited::near_heap_limit_callback(std::size_t current_heap_l
42
42
  auto& executor = agent_host::get_current(isolate).executor();
43
43
  executor.terminate();
44
44
  }
45
- // Increase heap limit 64mb at a time
46
- return current_heap_limit + (64 << 20);
45
+ // Increase heap limit 80mb at a time. Experimentally derived.
46
+ return current_heap_limit + (80 << 20);
47
47
  }
48
48
 
49
49
  } // namespace js::iv8::isolated
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@auto_js/v8",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
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",
9
- "@auto_js/v8_exports": "^0.0.7"
8
+ "@auto_js/js": "^0.0.10",
9
+ "@auto_js/v8_exports": "^0.0.8"
10
10
  },
11
11
  "repository": {
12
12
  "type": "git",
@@ -15,7 +15,7 @@ struct platform_instance_handle {
15
15
  util::lockable<platform_instance_handle> shared_platform;
16
16
 
17
17
  platform_handle::platform_handle(const std::type_info& type, make_type* make) :
18
- platform_{util::elide{[ & ]() -> std::shared_ptr<v8::Platform> {
18
+ platform_{util::elide{[ & ] -> std::shared_ptr<v8::Platform> {
19
19
  auto lock = shared_platform.write();
20
20
  auto acquired_platform = lock->platform.lock();
21
21
  if (acquired_platform) {
@@ -83,7 +83,7 @@ export class instrumentation_delegate : virtual public v8::Platform {
83
83
 
84
84
  template <class Delegate>
85
85
  auto platform_handle::acquire() -> platform_handle {
86
- constexpr auto make = []() -> std::shared_ptr<v8::Platform> {
86
+ constexpr auto make = [] -> std::shared_ptr<v8::Platform> {
87
87
  return std::make_shared<Delegate>();
88
88
  };
89
89
  return platform_handle{typeid(Delegate), make};
@@ -35,13 +35,13 @@ auto foreground_runner::terminate() -> void {
35
35
  auto foreground_runner::get_for_priority(std::shared_ptr<foreground_runner> self, v8::TaskPriority priority) -> std::shared_ptr<v8::TaskRunner> {
36
36
  return util::template_switch(
37
37
  priority_numeric_from(priority),
38
- util::sequence<priority_count>,
38
+ util::sequence_cw<priority_count>,
39
39
  util::overloaded{
40
40
  [ & ](auto priority) -> std::shared_ptr<v8::TaskRunner> {
41
41
  using priority_runner_of = foreground_runner_of_priority<priority_enum_from(priority)>;
42
42
  return std::static_pointer_cast<priority_runner_of>(std::move(self));
43
43
  },
44
- []() -> std::shared_ptr<v8::TaskRunner> {
44
+ [] -> std::shared_ptr<v8::TaskRunner> {
45
45
  throw std::logic_error{"Invalid 'foreground_runner' priority"};
46
46
  },
47
47
  }
@@ -138,7 +138,7 @@ auto scheduler_foreground_thread::controller::run(
138
138
  });
139
139
  const auto any_stop_token = util::composite_stop_token{std::move(stop_token), termination_stop_token};
140
140
  while (!any_stop_token.stop_requested()) {
141
- auto [... timeout ] = [ & ]() -> auto {
141
+ auto [... timeout ] = [ & ] -> auto {
142
142
  if constexpr (requires { typename queue_type::clock_type; }) {
143
143
  return std::tuple{lock->queue.flush(queue_type::clock_type::now())};
144
144
  } else {
@@ -192,7 +192,7 @@ auto scheduler_background_threads::controller::run(
192
192
  });
193
193
  const auto any_stop_token = util::composite_stop_token{std::move(stop_token), termination_stop_token};
194
194
  while (!any_stop_token.stop_requested()) {
195
- auto [... timeout ] = [ & ]() -> auto {
195
+ auto [... timeout ] = [ & ] -> auto {
196
196
  if constexpr (requires { typename queue_type::clock_type; }) {
197
197
  return std::tuple{lock->queue.flush(queue_type::clock_type::now())};
198
198
  } else {
package/platform/task.cc CHANGED
@@ -43,7 +43,7 @@ class task_of final : public v8::Task {
43
43
 
44
44
  template <class... Args>
45
45
  auto make_task_of(std::invocable<std::stop_token, Args...> auto callback, Args&&... args) -> std::unique_ptr<v8::Task> {
46
- auto task = [ & ]() -> auto {
46
+ auto task = [ & ] -> auto {
47
47
  if constexpr (sizeof...(Args) == 0) {
48
48
  return std::move(callback);
49
49
  } else {
@@ -29,8 +29,8 @@ constexpr auto make_free_function_with_try_catch =
29
29
  auto bound_function = util::bind{
30
30
  [](callback_type& callback, Lock lock, const v8::FunctionCallbackInfo<v8::Value>& info) noexcept(Nx) -> void {
31
31
  // NOLINTNEXTLINE(cppcoreguidelines-slicing)
32
- auto result = invoke_internal_error_scope(lock, [ & ]() -> auto {
33
- auto run = util::regular_return{[ & ]() -> decltype(auto) {
32
+ auto result = invoke_internal_error_scope(lock, [ & ] -> auto {
33
+ auto run = util::regular_return{[ & ] -> decltype(auto) {
34
34
  return std::apply(
35
35
  callback,
36
36
  std::tuple_cat(
@@ -61,7 +61,7 @@ constexpr auto make_free_function_noexcept =
61
61
  using callback_type = decltype(callback);
62
62
  auto bound_function = util::bind{
63
63
  [](callback_type& callback, Lock lock, const v8::FunctionCallbackInfo<v8::Value>& /*info*/) noexcept -> void {
64
- auto run = util::regular_return{[ & ]() -> decltype(auto) {
64
+ auto run = util::regular_return{[ & ] -> decltype(auto) {
65
65
  return callback(lock);
66
66
  }};
67
67
  return js::transfer_in_strict<v8::Local<v8::Value>>(run().value_or(std::monostate{}), lock);
@@ -81,7 +81,7 @@ auto make_callback_storage(const auto& lock, auto function) {
81
81
  return std::tuple{callback, data};
82
82
  } else if constexpr (sizeof(bound_type) == sizeof(void*)) {
83
83
  // Trivial function of pointer type. Data() is the function data.
84
- auto data = [ & ]() -> auto {
84
+ auto data = [ & ] -> auto {
85
85
  #if V8_HAS_TAGGED_EXTERNAL
86
86
  return v8::External::New(lock.isolate(), std::bit_cast<void*>(bound), 0);
87
87
  #else
@@ -89,7 +89,7 @@ auto make_callback_storage(const auto& lock, auto function) {
89
89
  #endif
90
90
  }();
91
91
  const auto callback = v8::FunctionCallback{[](const v8::FunctionCallbackInfo<v8::Value>& info) -> void {
92
- auto invoke = [ & ]() -> auto {
92
+ auto invoke = [ & ] -> auto {
93
93
  #if V8_HAS_TAGGED_EXTERNAL
94
94
  // TODO: Use this feature
95
95
  return std::bit_cast<bound_type>(info.Data().As<v8::External>()->Value(0));
@@ -17,7 +17,7 @@ auto externalize_caught_error(context_lock_witness lock, v8::Local<v8::Value> va
17
17
  // Catch `iv8::pending_error` or `js::error`, converting to thrown v8 runtime error.
18
18
  [[nodiscard]] auto invoke_internal_error_scope(context_lock_witness lock, auto operation) {
19
19
  using result_type = std::invoke_result_t<decltype(operation)>;
20
- using value_type = type_t<[]() {
20
+ using value_type = type_t<[] {
21
21
  if constexpr (type<result_type> == type<void>) {
22
22
  return type<bool>;
23
23
  } else {
@@ -89,7 +89,7 @@ auto accept_v8_value::operator()(date_tag /*tag*/, visit_holder /*visit*/, js_cl
89
89
  auto accept_v8_value::operator()(error_tag /*tag*/, visit_holder /*visit*/, const js::error_value& subject) const
90
90
  -> js::referenceable_value<v8::Local<v8::Object>> {
91
91
  auto message = js::transfer_in<v8::Local<v8::String>>(subject.message(), witness());
92
- auto error = v8::Local<v8::Value>{[ & ]() -> v8::Local<v8::Value> {
92
+ auto error = v8::Local<v8::Value>{[ & ] -> v8::Local<v8::Value> {
93
93
  switch (subject.name()) {
94
94
  // These functions don't need an isolate somehow?
95
95
  default:
@@ -131,7 +131,7 @@ auto accept_v8_value::operator()(array_buffer_tag /*tag*/, visit_holder /*visit*
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
133
  auto byte_length = subject.byte_length();
134
- auto backing_store = [ & ]() -> auto {
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
137
137
  // trick does not work in that case.
@@ -16,10 +16,10 @@ constexpr auto make_free_function(auto function);
16
16
 
17
17
  // Reference acceptor
18
18
  struct reaccept_v8_value {
19
- using reference_type = v8::Local<v8::Value>;
19
+ using reference_type = v8::Local<v8::Data>;
20
20
 
21
21
  template <class Type>
22
- constexpr auto operator()(std::type_identity<v8::Local<Type>> /*type*/, v8::Local<v8::Value> subject) const -> v8::Local<Type> {
22
+ constexpr auto operator()(std::type_identity<v8::Local<Type>> /*type*/, v8::Local<v8::Data> subject) const -> v8::Local<Type> {
23
23
  return subject.As<Type>();
24
24
  }
25
25
  };
@@ -63,6 +63,12 @@ struct accept_v8_primitive {
63
63
  return (*this)(number_tag_of<double>{}, visit, double{std::forward<decltype(subject)>(subject)});
64
64
  }
65
65
 
66
+ template <class Type>
67
+ auto operator()(number_tag_of<Type> tag, visit_holder visit, Type subject) const -> v8::Local<tag_to_v8<number_tag_of<Type>>> {
68
+ auto value = (*this)(tag, visit, std::int32_t{std::forward<decltype(subject)>(subject)});
69
+ return v8::Local<v8::Int32>{value}.As<tag_to_v8<number_tag_of<Type>>>();
70
+ }
71
+
66
72
  template <class Type>
67
73
  auto operator()(number_tag_of<Type> tag, visit_holder visit, auto&& subject) const -> v8::Local<tag_to_v8<number_tag_of<Type>>> {
68
74
  return (*this)(tag, visit, Type{std::forward<decltype(subject)>(subject)});
@@ -167,12 +173,10 @@ struct accept_v8_value : accept_v8_primitive {
167
173
  [](v8::Local<v8::Array> array, auto& self, auto& visit, auto /*&&*/ subject) -> void {
168
174
  auto&& range = util::into_range(std::forward<decltype(subject)>(subject));
169
175
  for (auto&& [ key, value ] : util::forward_range(std::forward<decltype(range)>(range))) {
170
- auto result = array->Set(
171
- self.context_,
172
- visit.first(std::forward<decltype(key)>(key), self),
173
- visit.second(std::forward<decltype(value)>(value), self)
174
- );
175
- unmaybe(result);
176
+ // nb: 'first' must run before 'second'
177
+ auto name = visit.first(std::forward<decltype(key)>(key), self);
178
+ auto item = visit.second(std::forward<decltype(value)>(value), self);
179
+ unmaybe(array->Set(self.context_, name, item));
176
180
  }
177
181
  },
178
182
  };
@@ -203,8 +207,8 @@ struct accept_v8_value : accept_v8_primitive {
203
207
  v8::Array::New(self.isolate(), Size),
204
208
  std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
205
209
  [](v8::Local<v8::Array> array, auto& self, auto& visit, auto /*&&*/ tuple) -> void {
206
- const auto [... indices ] = util::sequence<Size>;
207
- (..., [ & ]() -> void {
210
+ constexpr auto [... indices ] = util::sequence<Size>;
211
+ (..., [ & ] -> void {
208
212
  auto element = visit(indices, std::forward<decltype(tuple)>(tuple), self);
209
213
  unmaybe(array->Set(self.context_, indices, element));
210
214
  }());
@@ -221,12 +225,10 @@ struct accept_v8_value : accept_v8_primitive {
221
225
  [](v8::Local<v8::Object> object, auto& self, auto& visit, auto /*&&*/ subject) -> void {
222
226
  auto&& range = util::into_range(std::forward<decltype(subject)>(subject));
223
227
  for (auto&& [ key, value ] : util::forward_range(std::forward<decltype(range)>(range))) {
224
- auto result = object->Set(
225
- self.context_,
226
- visit.first(std::forward<decltype(key)>(key), self),
227
- visit.second(std::forward<decltype(value)>(value), self)
228
- );
229
- unmaybe(result);
228
+ // nb: 'first' must run before 'second'
229
+ auto name = visit.first(std::forward<decltype(key)>(key), self);
230
+ auto item = visit.second(std::forward<decltype(value)>(value), self);
231
+ unmaybe(object->Set(self.context_, name, item));
230
232
  }
231
233
  },
232
234
  };
@@ -281,8 +283,8 @@ struct accept_v8_value : accept_v8_primitive {
281
283
 
282
284
  template <std::size_t Size>
283
285
  auto accept_entry_pair_struct(this const auto& self, auto& visit, v8::Local<v8::Object> target, auto&& subject) -> void {
284
- const auto [... indices ] = util::sequence<Size>;
285
- (..., [ & ]() -> void {
286
+ constexpr auto [... indices ] = util::sequence<Size>;
287
+ (..., [ & ] -> void {
286
288
  auto accept_entry = accept_entry_pair<decltype(self), decltype(self)>{self};
287
289
  auto entry = visit(std::integral_constant<std::size_t, indices>{}, std::forward<decltype(subject)>(subject), accept_entry);
288
290
  unmaybe(target->Set(self.context_, entry.first, entry.second));
@@ -293,44 +295,56 @@ struct accept_v8_value : accept_v8_primitive {
293
295
  v8::Local<v8::Context> context_;
294
296
  };
295
297
 
296
- // `accept_v8_value` subclass, implementing specialized acceptor with lock type.
298
+ // Lock delegate for `accept_v8_value_with`. In the `context_lock_witness` case, `accept_v8_value`
299
+ // holds that lock by type. If the lock is more specialized we need to hold another reference.
297
300
  template <class Lock>
298
- struct accept_v8_value_with;
301
+ struct accept_v8_value_lock_delegate;
299
302
 
300
303
  template <>
301
- struct accept_v8_value_with<context_lock_witness> : accept_v8_value {
304
+ struct accept_v8_value_lock_delegate<context_lock_witness> : accept_v8_value {
302
305
  using accept_v8_value::accept_v8_value;
303
- using accept_v8_value::operator();
304
- using accept_v8_value::witness;
305
-
306
- // function instantiation
307
- auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, auto subject) const -> v8::Local<iv8::Function> {
308
- auto [ function, length ] = make_free_function<context_lock_witness>(std::move(subject).callback);
309
- auto [ callback, data ] = make_callback_storage(witness(), std::move(function));
310
- return unmaybe(v8::Function::New(witness().context(), callback, data, length, v8::ConstructorBehavior::kThrow).template As<iv8::Function>());
311
- }
312
306
  };
313
307
 
314
308
  template <class Lock>
315
- struct accept_v8_value_with : accept_v8_value {
309
+ struct accept_v8_value_lock_delegate : accept_v8_value {
316
310
  public:
317
- accept_v8_value_with(auto* transfer, const Lock& lock) :
311
+ accept_v8_value_lock_delegate(auto* transfer, const Lock& lock) :
318
312
  accept_v8_value{transfer, lock},
319
313
  lock_{lock} {}
320
314
 
321
- using accept_v8_value::operator();
322
-
323
- // function instantiation
324
- auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, auto subject) const -> v8::Local<iv8::Function> {
325
- auto [ function, length ] = make_free_function<Lock>(std::move(subject).callback);
326
- auto [ callback, data ] = make_callback_storage(lock_.get(), std::move(function));
327
- return v8::Function::New(lock_.get().context(), callback, data, length, v8::ConstructorBehavior::kThrow);
328
- }
315
+ [[nodiscard]] auto witness() const -> const Lock& { return lock_; }
329
316
 
330
317
  private:
331
318
  std::reference_wrapper<const Lock> lock_;
332
319
  };
333
320
 
321
+ // `accept_v8_value` subclass, implementing specialized acceptor with lock type.
322
+ template <class Lock>
323
+ struct accept_v8_value_with : accept_v8_value_lock_delegate<Lock> {
324
+ using accept_v8_value_lock_delegate<Lock>::accept_v8_value_lock_delegate;
325
+ using accept_v8_value_lock_delegate<Lock>::operator();
326
+ using accept_v8_value_lock_delegate<Lock>::witness;
327
+
328
+ // object template instantiation
329
+ auto operator()(object_prototype_tag /*tag*/, visit_holder /*visit*/, v8::Local<v8::ObjectTemplate> subject) const -> v8::Local<v8::Object> {
330
+ return unmaybe(subject->NewInstance(witness().context()));
331
+ }
332
+
333
+ // function instantiation (from template)
334
+ auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, v8::Local<v8::FunctionTemplate> subject) const -> v8::Local<iv8::Function> {
335
+ auto fn = unmaybe(subject->GetFunction(witness().context()));
336
+ return v8::Local<v8::Function>{fn}.As<iv8::Function>();
337
+ }
338
+
339
+ // function instantiation (from native type)
340
+ auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, auto subject) const -> v8::Local<iv8::Function> {
341
+ auto [ function, length ] = make_free_function<context_lock_witness>(std::move(subject).callback);
342
+ auto [ callback, data ] = make_callback_storage(witness(), std::move(function));
343
+ auto fn = unmaybe(v8::Function::New(witness().context(), callback, data, length, v8::ConstructorBehavior::kThrow));
344
+ return v8::Local<v8::Function>{fn}.As<iv8::Function>();
345
+ }
346
+ };
347
+
334
348
  // Acceptor with template environment
335
349
  template <class Meta, class Lock>
336
350
  struct accept_v8_template : accept_v8_primitive {
@@ -339,6 +353,36 @@ struct accept_v8_template : accept_v8_primitive {
339
353
  accept_v8_primitive{transfer, lock},
340
354
  lock_{lock} {}
341
355
 
356
+ // cast primitive to prototype
357
+ template <std::convertible_to<primitive_tag> Tag>
358
+ auto operator()(Tag tag, auto& visit, auto&& subject) const -> v8::Local<v8::Template>
359
+ // refuse on bigint, which requires a context for some reason.
360
+ requires(!std::convertible_to<Tag, bigint_tag>) {
361
+ const accept_v8_primitive& accept = *this;
362
+ auto result = accept(tag, visit, std::forward<decltype(subject)>(subject));
363
+ auto value = v8::Local<v8::Value>{dispatch_referenceable(result)};
364
+ // nb: We are smuggling a `v8::Primitive` as a `v8::Template`
365
+ return std::bit_cast<v8::Local<v8::Template>>(value);
366
+ }
367
+
368
+ // object
369
+ auto operator()(this const auto& self, dictionary_tag /*tag*/, auto& visit, auto&& subject)
370
+ -> js::deferred_receiver<v8::Local<v8::ObjectTemplate>, decltype(self), decltype(visit), decltype(subject)> {
371
+ return {
372
+ v8::ObjectTemplate::New(self.isolate()),
373
+ std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
374
+ [](v8::Local<v8::ObjectTemplate> object, auto& self, auto& visit, auto /*&&*/ subject) -> void {
375
+ auto&& range = util::into_range(std::forward<decltype(subject)>(subject));
376
+ for (auto&& [ key, value ] : util::forward_range(std::forward<decltype(range)>(range))) {
377
+ // nb: 'first' must run before 'second'
378
+ auto name = v8::Local<v8::Template>{visit.first(std::forward<decltype(key)>(key), self)};
379
+ auto item = visit.second(std::forward<decltype(value)>(value), self);
380
+ object->Set(name.As<v8::Name>(), item);
381
+ }
382
+ },
383
+ };
384
+ }
385
+
342
386
  // function template
343
387
  auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, auto subject) const -> v8::Local<v8::FunctionTemplate> {
344
388
  auto [ function, length ] = make_free_function<Lock>(std::move(subject).callback);
package/transfer/visit.cc CHANGED
@@ -25,7 +25,7 @@ struct visit_property_name {
25
25
 
26
26
  template <class Accept>
27
27
  auto operator()(v8::Local<v8::Primitive> subject, const Accept& accept) -> accept_target_t<Accept> {
28
- return visit_.get().lookup_or_visit(subject, [ & ]() -> accept_target_t<Accept> {
28
+ return visit_.get().lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
29
29
  auto accept_as = [ & ]<class Tag>(Tag tag) -> auto {
30
30
  auto value = value_of{witness(), subject.As<tag_to_v8<Tag>>()};
31
31
  return accept(tag, *this, value);
@@ -80,7 +80,7 @@ struct visit_cached_immediate : Visit {
80
80
  auto operator()(auto subject, const Accept& accept) -> accept_target_t<Accept>
81
81
  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124954
82
82
  requires requires { this->immediate(subject, accept); } {
83
- return lookup_or_visit(subject, [ & ]() -> accept_target_t<Accept> {
83
+ return lookup_or_visit(subject, [ & ] -> accept_target_t<Accept> {
84
84
  return immediate(subject, accept);
85
85
  });
86
86
  }
@@ -276,7 +276,7 @@ struct visit_value : visit_flat_value<Target> {
276
276
  auto operator()(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 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{
@@ -313,7 +313,7 @@ struct visit_value : visit_flat_value<Target> {
313
313
  [](auto /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
314
314
 
315
315
  // Slow path
316
- [ & ]() -> accept_target_t<Accept> {
316
+ [ & ] -> accept_target_t<Accept> {
317
317
  if (subject->IsObject()) {
318
318
  return immediate(subject.As<v8::Object>(), accept);
319
319
  } else {
@@ -327,7 +327,7 @@ struct visit_value : visit_flat_value<Target> {
327
327
 
328
328
  template <class Accept>
329
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> {
330
+ return 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{
@@ -337,7 +337,7 @@ struct visit_value : visit_flat_value<Target> {
337
337
  [ & ](shared_array_buffer_tag /*tag*/, auto next) -> accept_target_t<Accept> { return next(); },
338
338
 
339
339
  // Slow path
340
- [ & ]() -> accept_target_t<Accept> {
340
+ [ & ] -> accept_target_t<Accept> {
341
341
  return immediate(subject, accept);
342
342
  }
343
343
  }
@@ -471,6 +471,17 @@ struct visit_v8_value_of {
471
471
  struct visit_template {
472
472
  explicit visit_template(auto* /*transfer*/) {}
473
473
 
474
+ template <class Accept>
475
+ auto operator()(v8::Local<v8::Template> subject, const Accept& accept) const -> accept_target_t<Accept> {
476
+ if (subject->IsFunctionTemplate()) {
477
+ return (*this)(subject.As<v8::FunctionTemplate>(), accept);
478
+ } else if (subject->IsObjectTemplate()) {
479
+ return (*this)(subject.As<v8::ObjectTemplate>(), accept);
480
+ } else {
481
+ return accept_target_t<Accept>{subject};
482
+ }
483
+ }
484
+
474
485
  template <class Accept>
475
486
  auto operator()(v8::Local<v8::FunctionTemplate> subject, const Accept& accept) const -> accept_target_t<Accept> {
476
487
  return accept(function_prototype_tag{}, *this, subject);
package/value/function.cc CHANGED
@@ -10,11 +10,12 @@ namespace js::iv8 {
10
10
 
11
11
  template <class Result>
12
12
  auto Function::apply(context_lock_witness lock, auto&& args) -> Result {
13
- auto result = [ & ]() {
13
+ auto result = [ & ] {
14
14
  using args_type = std::vector<v8::Local<v8::Value>>;
15
15
  auto handle_scope = v8::EscapableHandleScope{lock.isolate()};
16
16
  auto undefined = js::transfer_in_strict<v8::Local<v8::Value>>(std::monostate{}, lock);
17
17
  auto argv = js::transfer_in_strict<args_type>(std::forward<decltype(args)>(args), lock);
18
+ auto allow_js = v8::Isolate::AllowJavascriptExecutionScope{lock.isolate()};
18
19
  auto result = unmaybe(v8::Function::Call(lock.context(), undefined, argv.size(), argv.data()));
19
20
  return handle_scope.Escape(result);
20
21
  }();
@@ -23,7 +24,7 @@ auto Function::apply(context_lock_witness lock, auto&& args) -> Result {
23
24
 
24
25
  template <class Result>
25
26
  auto Function::call(context_lock_witness lock, auto&&... args) -> Result {
26
- auto result = [ & ]() {
27
+ auto result = [ & ] {
27
28
  using args_type = std::array<v8::Local<v8::Value>, sizeof...(args) + 1>;
28
29
  auto handle_scope = v8::EscapableHandleScope{lock.isolate()};
29
30
  auto argv = js::transfer_in_strict<args_type>(
@@ -33,6 +34,7 @@ auto Function::call(context_lock_witness lock, auto&&... args) -> Result {
33
34
  ),
34
35
  lock
35
36
  );
37
+ auto allow_js = v8::Isolate::AllowJavascriptExecutionScope{lock.isolate()};
36
38
  return handle_scope.Escape(unmaybe(v8::Function::Call(lock.context(), argv[ 0 ], argv.size() - 1, argv.data() + 1)));
37
39
  }();
38
40
  return js::transfer_out<Result>(result, lock);