@auto_js/v8 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CMakeLists.txt CHANGED
@@ -63,6 +63,7 @@ if(v8_js STREQUAL isolated_v8_js)
63
63
  isolated/agent.cc
64
64
  isolated/clock.cc
65
65
  isolated/cluster.cc
66
+ isolated/executor.cc
66
67
  isolated/platform.cc
67
68
  platform/delegate.cc
68
69
  platform/foreground_runner.cc
@@ -74,8 +75,8 @@ if(v8_js STREQUAL isolated_v8_js)
74
75
  isolated/agent.h.cc
75
76
  isolated/clock.h.cc
76
77
  isolated/cluster.h.cc
78
+ isolated/executor.h.cc
77
79
  isolated/platform.h.cc
78
- platform/allocator.h.cc
79
80
  platform/delegate.h.cc
80
81
  platform/foreground_runner.h.cc
81
82
  platform/scheduler.cc
@@ -20,11 +20,9 @@ auto script::compile(context_lock_witness lock, v8::Local<v8::String> code_strin
20
20
  });
21
21
  }
22
22
 
23
- auto script::run(context_lock_witness lock, v8::Local<v8::UnboundScript> script) -> expected_value_type {
24
- return invoke_externalized_error_scope(lock, [ & ]() -> js::value_t {
25
- auto maybe_result = script->BindToCurrentContext()->Run(lock.context());
26
- return js::transfer_out<js::value_t>(unmaybe(maybe_result), lock);
27
- });
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);
28
26
  }
29
27
 
30
28
  } // namespace js::iv8
@@ -10,12 +10,12 @@ namespace js::iv8 {
10
10
  export struct script {
11
11
  public:
12
12
  using expected_script_type = std::expected<v8::Local<v8::UnboundScript>, js::error_value>;
13
- using expected_value_type = std::expected<js::value_t, js::error_value>;
13
+ using expected_value_type = std::optional<std::expected<js::value_t, js::error_value>>;
14
14
 
15
15
  // nb: It is undocumented (and even mentions "context independent"), but the script compiler
16
16
  // actually needs a context because it can throw an error and *that* would need a context.
17
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) -> expected_value_type;
18
+ static auto run(context_lock_witness lock, v8::Local<v8::UnboundScript> script) -> v8::Local<v8::Value>;
19
19
 
20
20
  private:
21
21
  static auto compile(context_lock_witness lock, v8::Local<v8::String> code_string, source_origin source_origin) -> expected_script_type;
@@ -1,3 +1,5 @@
1
+ module;
2
+ #include <v8_js/version.h>
1
3
  export module v8_js:collected_handle;
2
4
  import :lock;
3
5
  import std;
@@ -64,7 +66,7 @@ auto collected_handle<Value, Type>::reset(const collected_handle_lock& lock, uni
64
66
  [](const v8::WeakCallbackInfo<collected_handle>& data) -> auto {
65
67
  auto* ptr = data.GetParameter();
66
68
  auto unique = ptr->pool_.get().reacquire(ptr);
67
- unique->global_.ClearWeak();
69
+ unique->global_.template ClearWeak<void>();
68
70
  },
69
71
  v8::WeakCallbackType::kParameter
70
72
  );
@@ -74,7 +76,11 @@ auto collected_handle<Value, Type>::reset(const collected_handle_lock& lock, uni
74
76
  export template <class Type>
75
77
  auto make_collected_external(const collected_handle_lock& lock, auto&&... args) -> v8::Local<v8::External> {
76
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
77
82
  auto value = v8::External::New(lock.isolate(), unique.get());
83
+ #endif
78
84
  collected_handle<v8::External, Type>::reset(lock, std::move(unique), value);
79
85
  return value;
80
86
  }
@@ -82,7 +88,12 @@ auto make_collected_external(const collected_handle_lock& lock, auto&&... args)
82
88
  export template <class Type>
83
89
  auto unwrap_collected_external(v8::Local<v8::External> external) -> Type& {
84
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
85
95
  return **static_cast<handle_type*>(external->Value());
96
+ #endif
86
97
  }
87
98
 
88
99
  } // namespace js::iv8
package/isolated/agent.cc CHANGED
@@ -2,7 +2,6 @@ module;
2
2
  #include <cassert>
3
3
  module v8_js;
4
4
  import :isolated.agent;
5
- import :platform.allocator;
6
5
  import std;
7
6
 
8
7
  namespace js::iv8::isolated {
@@ -25,7 +24,7 @@ auto agent_storage::release(std::shared_ptr<agent_storage> storage) -> void {
25
24
  agent_handle::agent_handle(std::shared_ptr<agent_host> host) :
26
25
  host_{host} {
27
26
  if (host && host->handle_count_.fetch_add(1, std::memory_order_relaxed) == 0) {
28
- *host->self_handle_.write() = std::move(host);
27
+ host->self_handle_.store(std::move(host), std::memory_order_relaxed);
29
28
  }
30
29
  }
31
30
 
@@ -46,12 +45,29 @@ agent_handle::~agent_handle() {
46
45
  host->handle_count_.load(std::memory_order_acquire) == 1 ||
47
46
  host->handle_count_.fetch_sub(1, std::memory_order_acq_rel) == 1
48
47
  ) {
49
- host->self_handle_.write()->reset();
48
+ host->self_handle_.store(nullptr, std::memory_order_relaxed);
50
49
  }
51
50
  }
52
51
  }
53
52
  }
54
53
 
54
+ auto agent_handle::dispose(dispose_callback_type after_callback) -> void {
55
+ if (auto host = host_.lock()) {
56
+ assert(!host->dispose_callback_);
57
+ host->dispose_callback_ = std::move(after_callback);
58
+ host->executor_.terminate();
59
+ host->self_handle_.store(nullptr, std::memory_order_release);
60
+ }
61
+ }
62
+
63
+ auto agent_handle::lock_host() const -> std::shared_ptr<agent_host> {
64
+ auto host = host_.lock();
65
+ if (host && host->executor_.is_terminated()) {
66
+ host.reset();
67
+ }
68
+ return host;
69
+ }
70
+
55
71
  // `agent_host`
56
72
  agent_host::agent_host(
57
73
  destroy_callback_type destroy_callback,
@@ -59,27 +75,24 @@ agent_host::agent_host(
59
75
  behavior_params params
60
76
  ) :
61
77
  storage_{std::move(storage)},
62
- // TODO: v8 holds its own reference to this. I will probably need it later for per-isolate
63
- // memory diagnostics though?
64
- array_buffer_allocator_{std::make_shared<platform::data_block_allocator>()},
65
- isolate_{v8::Isolate::Allocate()},
66
78
  clock_{params.clock},
67
79
  destroy_callback_{destroy_callback},
68
80
  reset_handle_callback_{reset_handle_type{util::fn<&agent_host::remote_expiration_callback>, *this}},
69
81
  random_seed_{params.random_seed} {
70
- isolate_->SetData(0, this);
71
- auto create_params = v8::Isolate::CreateParams{};
72
- create_params.array_buffer_allocator_shared = array_buffer_allocator_;
73
- v8::Isolate::Initialize(isolate_.get(), create_params);
74
-
75
- isolate_->SetCaptureStackTraceForUncaughtExceptions(true);
82
+ executor_.initialize([ & ](v8::Isolate* isolate) noexcept -> auto {
83
+ isolate->SetData(0, this);
84
+ auto create_params = v8::Isolate::CreateParams{};
85
+ create_params.array_buffer_allocator_shared =
86
+ std::shared_ptr<v8::ArrayBuffer::Allocator>(v8::ArrayBuffer::Allocator::NewDefaultAllocator());
87
+ return create_params;
88
+ });
89
+ executor_.isolate()->SetCaptureStackTraceForUncaughtExceptions(true);
76
90
  }
77
91
 
78
92
  agent_host::~agent_host() {
79
93
  // Terminate foreground runner
80
94
  auto& scheduler = storage_->foreground_runner().scheduler();
81
95
  scheduler.terminate();
82
- assert(!scheduler.is_this_thread());
83
96
  if (!scheduler.is_this_thread()) {
84
97
  auto thread = scheduler.invoke([](auto& controller) -> auto {
85
98
  return controller.take();
@@ -88,19 +101,20 @@ agent_host::~agent_host() {
88
101
  }
89
102
  // Destroy isolate and remaining handles
90
103
  {
91
- auto lock = js::iv8::isolate_destructor_lock{isolate_.get()};
104
+ auto lock = js::iv8::isolate_destructor_lock{executor_.isolate()};
92
105
  scratch_context_.Reset();
93
- scheduler.mutate([ & ](platform::foreground_task_queue& queue) -> void {
94
- queue.finalize();
95
- });
96
106
  remote_handle_list_.clear(util::slice(lock));
97
107
  autorelease_pool_.clear();
98
108
  destroy_callback_(util::slice(lock));
99
109
  }
100
110
  // Deallocate isolate (before `agent_storage`)
101
- isolate_.reset();
111
+ executor_.reset();
102
112
  // Release storage
103
113
  agent_storage::release(std::move(storage_));
114
+ // Notify client
115
+ if (dispose_callback_) {
116
+ dispose_callback_();
117
+ }
104
118
  }
105
119
 
106
120
  auto agent_host::make_context() -> v8::Local<v8::Context> {
@@ -109,7 +123,7 @@ auto agent_host::make_context() -> v8::Local<v8::Context> {
109
123
  // the generator is initialized on context creation, so we just control the randomness in that one
110
124
  // case.
111
125
  should_give_seed_ = true;
112
- auto context = v8::Context::New(isolate_.get());
126
+ auto context = v8::Context::New(executor_.isolate());
113
127
  should_give_seed_ = false;
114
128
  return context;
115
129
  }
@@ -122,7 +136,7 @@ auto agent_host::make_remote_handle_lock(isolate_lock_witness lock) -> remote_ha
122
136
  auto agent_host::remote_expiration_callback(expired_remote_type remote) noexcept -> void {
123
137
  storage_->foreground_runner().schedule_handle_task(
124
138
  [ this, remote = std::move(remote) ](std::stop_token /*stop_token*/) -> void {
125
- auto lock = isolate_execution_lock{isolate()};
139
+ auto lock = isolate_execution_lock{executor_.isolate()};
126
140
  remote->reset(util::slice(lock));
127
141
  remote_handle_list_.erase(*remote);
128
142
  }
@@ -132,11 +146,11 @@ auto agent_host::remote_expiration_callback(expired_remote_type remote) noexcept
132
146
  auto agent_host::scratch_context() -> v8::Local<v8::Context> {
133
147
  if (scratch_context_.IsEmpty()) {
134
148
  auto context = make_context();
135
- scratch_context_.Reset(isolate_.get(), context);
149
+ scratch_context_.Reset(executor_.isolate(), context);
136
150
  scratch_context_.SetWeak();
137
151
  return context;
138
152
  } else {
139
- return scratch_context_.Get(isolate_.get());
153
+ return scratch_context_.Get(executor_.isolate());
140
154
  }
141
155
  }
142
156
 
@@ -3,6 +3,7 @@ module;
3
3
  export module v8_js:isolated.agent;
4
4
  import :collected_handle;
5
5
  import :isolated.clock;
6
+ import :isolated.executor;
6
7
  import :platform.foreground_runner;
7
8
  import :remote;
8
9
  import std;
@@ -67,15 +68,18 @@ export class agent_storage {
67
68
  export class agent_handle {
68
69
  public:
69
70
  using lock = agent_lock;
71
+ using dispose_callback_type = util::move_only_function<auto() noexcept -> void>;
70
72
  explicit agent_handle(std::shared_ptr<agent_host> host);
71
- agent_handle(const agent_handle& /*handle*/);
73
+ agent_handle(const agent_handle& handle);
72
74
  agent_handle(agent_handle&&) = default;
73
75
  ~agent_handle();
74
76
  auto operator=(const agent_handle&) -> agent_handle& = delete;
75
77
  auto operator=(agent_handle&&) -> agent_handle& = default;
76
78
 
79
+ auto dispose(dispose_callback_type after_callback) -> void;
80
+
77
81
  protected:
78
- [[nodiscard]] auto lock_host() const -> std::shared_ptr<agent_host> { return host_.lock(); }
82
+ [[nodiscard]] auto lock_host() const -> std::shared_ptr<agent_host>;
79
83
 
80
84
  private:
81
85
  std::weak_ptr<agent_host> host_;
@@ -125,7 +129,7 @@ export class agent_host
125
129
 
126
130
  auto autorelease_pool() -> util::autorelease_pool& { return autorelease_pool_; }
127
131
  auto clock(this auto& self) -> auto& { return self.clock_.at(0); }
128
- auto isolate() -> v8::Isolate* { return isolate_.get(); }
132
+ auto executor() -> auto& { return executor_; }
129
133
  auto make_context() -> v8::Local<v8::Context>;
130
134
  auto make_remote_handle_lock(isolate_lock_witness lock) -> remote_handle_lock;
131
135
  auto scratch_context() -> v8::Local<v8::Context>;
@@ -137,15 +141,11 @@ export class agent_host
137
141
 
138
142
  private:
139
143
  friend class agent_handle;
140
- using lockable_handle_type = util::lockable_with<std::shared_ptr<agent_host>, {.shared = true}>;
141
-
142
144
  auto remote_expiration_callback(expired_remote_type remote) noexcept -> void;
143
- static auto dispose_isolate(v8::Isolate* isolate) -> void { isolate->Dispose(); }
144
145
 
145
146
  // Order matters for these members
146
147
  std::shared_ptr<agent_storage> storage_;
147
- std::shared_ptr<v8::ArrayBuffer::Allocator> array_buffer_allocator_;
148
- std::unique_ptr<v8::Isolate, util::function_constant<dispose_isolate>> isolate_;
148
+ isolated::executor executor_;
149
149
 
150
150
  // Order doesn't really matter
151
151
  bool should_give_seed_{};
@@ -157,8 +157,9 @@ export class agent_host
157
157
  // /gcc-out/../gcc/gcc/cp/class.cc:9510
158
158
  std::array<clock::any_clock, 1> clock_;
159
159
  destroy_callback_type destroy_callback_;
160
- lockable_handle_type self_handle_;
160
+ util::atomic_shared_ptr<agent_host> self_handle_;
161
161
  remote_handle_list remote_handle_list_;
162
+ agent_handle::dispose_callback_type dispose_callback_;
162
163
  std::array<reset_handle_type, 1> reset_handle_callback_;
163
164
  std::atomic<unsigned> handle_count_;
164
165
  std::optional<double> random_seed_;
@@ -195,7 +196,7 @@ auto agent_handle_of<Type>::schedule(Task task, Args... args) const -> void {
195
196
  auto& scheduler = host->storage()->foreground_runner();
196
197
  scheduler.schedule_client_task(
197
198
  [](std::stop_token /*stop_token*/, const auto& host, const auto& task, auto&&... args) -> void {
198
- auto isolate_lock = isolate_execution_lock{host->isolate()};
199
+ auto isolate_lock = isolate_execution_lock{host->executor().isolate()};
199
200
  std::visit([](auto& clock) -> void { clock.begin_tick(); }, host->clock());
200
201
  task(lock{isolate_lock, static_cast<agent_host_of<Type>&>(*host)}, std::forward<decltype(args)>(args)...);
201
202
  },
@@ -14,8 +14,9 @@ auto cluster::acquire_agent_storage() -> std::shared_ptr<agent_storage> {
14
14
  // NOLINTNEXTLINE(performance-unnecessary-value-param)
15
15
  auto cluster::release_agent_storage(std::shared_ptr<agent_storage> storage) -> void {
16
16
  agent_storage_.write()->erase(agent_storage_list_type::s_iterator_to(*storage));
17
- // I haven't seen this case yet.
18
- assert(!storage->foreground_runner().scheduler().is_this_thread());
17
+ if (storage->foreground_runner().scheduler().is_this_thread()) {
18
+ destroy_(std::any{std::move(storage)});
19
+ }
19
20
  }
20
21
 
21
22
  } // namespace js::iv8::isolated
@@ -8,16 +8,21 @@ namespace js::iv8::isolated {
8
8
 
9
9
  export class cluster {
10
10
  public:
11
- cluster() : platform_{isolated_platform::acquire()} {}
11
+ using synchronize_destroy = util::function_ref<auto(std::any)->void>;
12
+
13
+ explicit cluster(synchronize_destroy destroy) :
14
+ destroy_{destroy},
15
+ platform_{isolated_platform::acquire()} {}
16
+
12
17
  auto make_agent(auto make_environment, behavior_params params, auto callback) -> void;
13
18
  auto release_agent_storage(std::shared_ptr<agent_storage> storage) -> void;
14
19
 
15
20
  private:
16
21
  using intrusive_no_size = boost::intrusive::constant_time_size<false>;
17
22
  using agent_storage_list_type = boost::intrusive::list<agent_storage, intrusive_no_size, agent_storage::intrusive_hook>;
18
-
19
23
  auto acquire_agent_storage() -> std::shared_ptr<agent_storage>;
20
24
 
25
+ synchronize_destroy destroy_;
21
26
  util::lockable<agent_storage_list_type> agent_storage_;
22
27
  platform::platform_handle platform_;
23
28
  };
@@ -36,7 +41,7 @@ auto cluster::make_agent(auto make_environment, behavior_params params, auto cal
36
41
  auto callback
37
42
  ) -> auto {
38
43
  auto host = std::make_shared<agent_host_of<environment_type>>(std::move(storage), params);
39
- auto isolate_lock = isolate_execution_lock{host->isolate()};
44
+ auto isolate_lock = isolate_execution_lock{host->executor().isolate()};
40
45
  host->emplace_environment(util::elide{[ & ]() -> environment_type {
41
46
  return make_environment(agent_lock{isolate_lock, *host});
42
47
  }});
@@ -0,0 +1,31 @@
1
+ module v8_js;
2
+ import :isolated.agent;
3
+ import :isolated.executor;
4
+ import std;
5
+
6
+ namespace js::iv8::isolated {
7
+
8
+ auto executor::reset() -> void {
9
+ isolate_.reset();
10
+ }
11
+
12
+ auto executor::terminate() -> void {
13
+ terminated_.store(true, std::memory_order_release);
14
+ isolate_->TerminateExecution();
15
+ }
16
+
17
+ auto executor::is_terminated() const -> bool {
18
+ return terminated_.load(std::memory_order_acquire);
19
+ }
20
+
21
+ auto executor::on_before_call_entered(v8::Isolate* isolate) -> void {
22
+ // Checks for termination right after control has been passed to v8. This catches the case where a
23
+ // task is slow to start and `TerminateExecution()`, from `terminate()` is invoked on an idle
24
+ // isolate. In that case the termination is "handled" already.
25
+ auto& self = agent_host::get_current(isolate).executor();
26
+ if (self.is_terminated()) {
27
+ isolate->TerminateExecution();
28
+ }
29
+ }
30
+
31
+ } // namespace js::iv8::isolated
@@ -0,0 +1,34 @@
1
+ export module v8_js:isolated.executor;
2
+ import std;
3
+ import v8;
4
+
5
+ namespace js::iv8::isolated {
6
+
7
+ export class executor { // "En taro adun"
8
+ public:
9
+ auto initialize(std::invocable<v8::Isolate*> auto initialize) -> void;
10
+ auto reset() -> void;
11
+ auto terminate() -> void;
12
+
13
+ [[nodiscard]] auto is_terminated() const -> bool;
14
+ [[nodiscard]] auto isolate() const -> v8::Isolate* { return isolate_.get(); }
15
+
16
+ private:
17
+ constexpr static auto dispose_isolate = [](v8::Isolate* isolate) -> void { isolate->Dispose(); };
18
+ static auto on_before_call_entered(v8::Isolate* isolate) -> void;
19
+
20
+ std::unique_ptr<v8::Isolate, decltype(dispose_isolate)> isolate_;
21
+ std::atomic<bool> terminated_;
22
+ };
23
+
24
+ // ---
25
+
26
+ auto executor::initialize(std::invocable<v8::Isolate*> auto initialize) -> void {
27
+ static_assert(std::is_nothrow_invocable_v<decltype(initialize), v8::Isolate*>);
28
+ isolate_.reset(v8::Isolate::Allocate());
29
+ auto params = v8::Isolate::CreateParams{initialize(isolate_.get())};
30
+ v8::Isolate::Initialize(isolate_.get(), params);
31
+ isolate_->AddBeforeCallEnteredCallback(on_before_call_entered);
32
+ }
33
+
34
+ } // namespace js::iv8::isolated
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@auto_js/v8",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
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.6",
9
- "@auto_js/v8_exports": "^0.0.4"
8
+ "@auto_js/v8_exports": "^0.0.6",
9
+ "@auto_js/js": "^0.0.8"
10
10
  },
11
11
  "repository": {
12
12
  "type": "git",
@@ -7,10 +7,7 @@ namespace js::iv8::platform {
7
7
  // `foreground_task_queue`
8
8
  foreground_task_queue::foreground_task_queue(foreground_task_queue&& other) noexcept :
9
9
  tasks_{std::move(other.tasks_)},
10
- delayed_tasks_{std::move(other.delayed_tasks_)},
11
- finalized_{other.finalized_} {
12
- other.finalized_ = true;
13
- }
10
+ delayed_tasks_{std::move(other.delayed_tasks_)} {}
14
11
 
15
12
  auto foreground_task_queue::flush(clock_type::time_point now) -> clock_type::time_point {
16
13
  // nb: As far as I can tell all delayed tasks are posted with `kUserVisible`. I'm not even sure
@@ -18,32 +15,16 @@ auto foreground_task_queue::flush(clock_type::time_point now) -> clock_type::tim
18
15
  return delayed_tasks_.flush(tasks_.at(priority_numeric_from(v8::TaskPriority::kUserVisible)), now);
19
16
  }
20
17
 
21
- auto foreground_task_queue::finalize() -> void {
22
- finalized_ = true;
23
- auto& handle_tasks = tasks_.at(priority_numeric_from(v8::TaskPriority::kUserBlocking));
24
- while (!handle_tasks.empty()) {
25
- auto& task = tasks_.front();
26
- task(std::stop_token{});
27
- tasks_.pop();
28
- };
29
- while (!tasks_.empty()) tasks_.pop();
30
- while (!delayed_tasks_.empty()) delayed_tasks_.pop();
31
- }
32
-
33
18
  auto foreground_task_queue::front() -> task_entry_type& {
34
19
  return std::ranges::find_if_not(tasks_.containers(), &queue_type::empty)->front();
35
20
  }
36
21
 
37
22
  auto foreground_task_queue::push_delayed(delayed_entry_type task) -> void {
38
- if (!finalized_) {
39
- delayed_tasks_.emplace(std::move(task));
40
- }
23
+ delayed_tasks_.emplace(std::move(task));
41
24
  }
42
25
 
43
26
  auto foreground_task_queue::push(v8::TaskPriority priority, task_entry_type task) -> void {
44
- if (!finalized_) {
45
- tasks_.emplace(priority_numeric_from(priority), std::move(task));
46
- }
27
+ tasks_.emplace(priority_numeric_from(priority), std::move(task));
47
28
  }
48
29
 
49
30
  // `foreground_runner`
@@ -25,7 +25,6 @@ class foreground_task_queue {
25
25
  auto operator=(foreground_task_queue&&) -> foreground_task_queue& = delete;
26
26
 
27
27
  [[nodiscard]] auto empty() const -> bool { return tasks_.empty(); }
28
- auto finalize() -> void;
29
28
  auto flush(clock_type::time_point now) -> clock_type::time_point;
30
29
  auto front() -> task_entry_type&;
31
30
  auto pop() -> void { tasks_.pop(); }
@@ -35,7 +34,6 @@ class foreground_task_queue {
35
34
  private:
36
35
  util::segmented_priority_queue<queue_type, priority_count> tasks_;
37
36
  delayed_queue_type delayed_tasks_;
38
- bool finalized_ = false;
39
37
  };
40
38
 
41
39
  // Foreground task runner. Combines scheduler and task queue.
@@ -6,7 +6,7 @@ using std::chrono::steady_clock;
6
6
 
7
7
  // NOTE:
8
8
  // There is nothing interesting in this file. The main thing is that `task_runner_of` automatically
9
- // defines `NonNestableTasksEnabled` (etc) for you based on whether or not `post_non_nestable` (etc)
9
+ // defines `NonNestableTasksEnabled` (etc) -> for you based on whether or not `post_non_nestable` (etc)
10
10
  // is defined.
11
11
 
12
12
  namespace js::iv8::platform {
@@ -1,3 +1,5 @@
1
+ module;
2
+ #include <v8_js/version.h>
1
3
  export module v8_js:callback_storage;
2
4
  import :collected_handle;
3
5
  import :unmaybe;
@@ -71,7 +73,7 @@ auto make_callback_storage(const auto& lock, auto function) {
71
73
  static_assert(std::is_trivially_destructible_v<bound_type>);
72
74
  if constexpr (std::is_empty_v<bound_type>) {
73
75
  // Constant expression function of 0 size. No data needed at all!
74
- auto data = v8::Local<v8::Data>{};
76
+ auto data = v8::Local<v8::Value>{};
75
77
  const auto callback = v8::FunctionCallback{[](const v8::FunctionCallbackInfo<v8::Value>& info) -> void {
76
78
  auto invoke = bound_type{};
77
79
  invoke(info);
@@ -79,9 +81,22 @@ auto make_callback_storage(const auto& lock, auto function) {
79
81
  return std::tuple{callback, data};
80
82
  } else if constexpr (sizeof(bound_type) == sizeof(void*)) {
81
83
  // Trivial function of pointer type. Data() is the function data.
82
- auto data = v8::External::New(lock.isolate(), std::bit_cast<void*>(bound));
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
+ }();
83
91
  const auto callback = v8::FunctionCallback{[](const v8::FunctionCallbackInfo<v8::Value>& info) -> void {
84
- auto invoke = std::bit_cast<bound_type>(info.Data().As<v8::External>()->Value());
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
+ }();
85
100
  invoke(info);
86
101
  }};
87
102
  return std::tuple{callback, data};
@@ -40,24 +40,29 @@ auto externalize_caught_error(context_lock_witness lock, v8::Local<v8::Value> va
40
40
  }
41
41
  };
42
42
 
43
- // Catch pending js errors and externalize them as `js::error_value`. `std::expected<T,
44
- // js::error_value>` is returned.
43
+ // Catch pending js errors and externalize them as `js::error_value`.
44
+ // `std::optional<std::expected<T, js::error_value>>` is returned. The outer optional is nullopt in
45
+ // the case of terminating execution.
45
46
  export [[nodiscard]] auto invoke_externalized_error_scope(context_lock_witness lock, auto operation) {
46
47
  using result_type = decltype(operation());
47
- using value_type = std::expected<result_type, js::error_value>;
48
+ using value_type = std::optional<std::expected<result_type, js::error_value>>;
48
49
  const auto try_catch = v8::TryCatch{lock.isolate()};
49
50
  try {
50
51
  if constexpr (type<result_type> == type<void>) {
51
52
  operation();
52
- return value_type{std::in_place};
53
+ return value_type{std::in_place, std::in_place};
53
54
  } else {
54
- return value_type{std::in_place, operation()};
55
+ return value_type{std::in_place, std::in_place, operation()};
55
56
  }
56
57
  } catch (const iv8::pending_error& /*error*/) {
57
58
  assert(try_catch.HasCaught());
58
- return value_type{std::unexpect, externalize_caught_error(lock, try_catch)};
59
+ if (lock.isolate()->IsExecutionTerminating()) {
60
+ return value_type{std::nullopt};
61
+ } else {
62
+ return value_type{std::in_place, std::unexpect, externalize_caught_error(lock, try_catch)};
63
+ }
59
64
  } catch (const js::error& error) {
60
- return value_type{std::unexpect, error};
65
+ return value_type{std::in_place, std::unexpect, error};
61
66
  }
62
67
  }
63
68
 
package/transfer/visit.cc CHANGED
@@ -486,7 +486,7 @@ struct visit<Meta, v8::FunctionCallbackInfo<v8::Value>> : visit<Meta, v8::Local<
486
486
  using visit_type::operator();
487
487
 
488
488
  template <class Accept>
489
- auto operator()(v8::FunctionCallbackInfo<v8::Value> info, const Accept& accept) -> accept_target_t<Accept> {
489
+ auto operator()(const v8::FunctionCallbackInfo<v8::Value>& info, const Accept& accept) -> accept_target_t<Accept> {
490
490
  return accept(vector_tag{}, *this, iv8::callback_info{info});
491
491
  }
492
492
  };
@@ -1,3 +1,5 @@
1
+ module;
2
+ #include <v8_js/version.h>
1
3
  module v8_js;
2
4
  import v8;
3
5
 
@@ -25,7 +27,11 @@ fixed_array::iterator::iterator(v8::Local<v8::FixedArray> array, v8::Local<v8::C
25
27
  index_{index} {}
26
28
 
27
29
  auto fixed_array::iterator::operator*() const -> value_type {
30
+ #if V8_HAS_CONTEXT_FREE_FIXED_ARRAY
31
+ return array_->Get(index_);
32
+ #else
28
33
  return array_->Get(context_, index_);
34
+ #endif
29
35
  }
30
36
 
31
37
  auto fixed_array::iterator::operator+=(difference_type offset) -> iterator& {
package/value/function.cc CHANGED
@@ -8,7 +8,7 @@ import v8;
8
8
 
9
9
  namespace js::iv8 {
10
10
 
11
- template <class Result = std::monostate>
11
+ template <class Result>
12
12
  auto Function::apply(context_lock_witness lock, auto&& args) -> Result {
13
13
  auto result = [ & ]() {
14
14
  using args_type = std::vector<v8::Local<v8::Value>>;
@@ -21,7 +21,7 @@ auto Function::apply(context_lock_witness lock, auto&& args) -> Result {
21
21
  return js::transfer_out<Result>(result, lock);
22
22
  }
23
23
 
24
- template <class Result = std::monostate>
24
+ template <class Result>
25
25
  auto Function::call(context_lock_witness lock, auto&&... args) -> Result {
26
26
  auto result = [ & ]() {
27
27
  using args_type = std::array<v8::Local<v8::Value>, sizeof...(args) + 1>;
@@ -1,3 +1,5 @@
1
+ module;
2
+ #include <v8_js/version.h>
1
3
  module v8_js;
2
4
  import :primitive;
3
5
  import std;
@@ -42,7 +44,7 @@ value_for_string::operator std::string() const {
42
44
  string.resize_and_overwrite((*this)->Length(), [ this ](char* data, std::size_t length) -> std::size_t {
43
45
  if (length > 0) {
44
46
  auto* data_uint8 = reinterpret_cast<std::uint8_t*>(data);
45
- (*this)->WriteOneByteV2(isolate(), 0, length, data_uint8, v8::String::WriteOptions::NO_NULL_TERMINATION);
47
+ (*this)->WriteOneByteV2(isolate(), 0, length, data_uint8, v8::String::WriteFlags::kNone);
46
48
  }
47
49
  return length;
48
50
  });
@@ -54,7 +56,7 @@ value_for_string::operator std::u16string() const {
54
56
  string.resize_and_overwrite((*this)->Length(), [ this ](char16_t* data, std::size_t length) -> std::size_t {
55
57
  if (length > 0) {
56
58
  auto* data_uint16 = reinterpret_cast<std::uint16_t*>(data);
57
- (*this)->WriteV2(isolate(), 0, length, data_uint16, v8::String::WriteOptions::NO_NULL_TERMINATION);
59
+ (*this)->WriteV2(isolate(), 0, length, data_uint16, v8::String::WriteFlags::kNone);
58
60
  }
59
61
  return length;
60
62
  });
@@ -68,7 +70,12 @@ value_for_date::operator js_clock::time_point() const {
68
70
 
69
71
  // `value_for_external`
70
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
71
77
  return (*this)->Value();
78
+ #endif
72
79
  }
73
80
 
74
81
  } // namespace js::iv8
@@ -1,22 +0,0 @@
1
- export module v8_js:platform.allocator;
2
- import std;
3
- import v8;
4
-
5
- namespace js::iv8::platform {
6
-
7
- class data_block_allocator : public v8::ArrayBuffer::Allocator {
8
- auto Allocate(std::size_t length) -> void* final {
9
- auto* data = AllocateUninitialized(length);
10
- std::memset(data, 0, length);
11
- return data;
12
- };
13
-
14
- auto AllocateUninitialized(std::size_t length) -> void* final { return new std::byte[ length ]; };
15
-
16
- auto GetPageAllocator() -> v8::PageAllocator* final { return nullptr; }
17
- void Free(void* data, std::size_t /*length*/) final { delete[] static_cast<std::byte*>(data); };
18
-
19
- [[nodiscard]] auto MaxAllocationSize() const -> std::size_t final { return std::numeric_limits<std::uint32_t>::max(); }
20
- };
21
-
22
- } // namespace js::iv8::platform