@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 +2 -1
- package/evaluation/script.cc +3 -5
- package/evaluation/script.h.cc +2 -2
- package/handle/collected_handle.cc +12 -1
- package/isolated/agent.cc +37 -23
- package/isolated/agent.h.cc +11 -10
- package/isolated/cluster.cc +3 -2
- package/isolated/cluster.h.cc +8 -3
- package/isolated/executor.cc +31 -0
- package/isolated/executor.h.cc +34 -0
- package/package.json +3 -3
- package/platform/foreground_runner.cc +3 -22
- package/platform/foreground_runner.h.cc +0 -2
- package/platform/task_runner.cc +1 -1
- package/support/callback_storage.cc +18 -3
- package/support/error.h.cc +12 -7
- package/transfer/visit.cc +1 -1
- package/value/fixed_array.cc +6 -0
- package/value/function.cc +2 -2
- package/value/primitive.cc +9 -2
- package/platform/allocator.h.cc +0 -22
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
|
package/evaluation/script.cc
CHANGED
|
@@ -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) ->
|
|
24
|
-
|
|
25
|
-
|
|
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
|
package/evaluation/script.h.cc
CHANGED
|
@@ -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) ->
|
|
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
|
-
|
|
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_.
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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{
|
|
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
|
-
|
|
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(
|
|
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(
|
|
149
|
+
scratch_context_.Reset(executor_.isolate(), context);
|
|
136
150
|
scratch_context_.SetWeak();
|
|
137
151
|
return context;
|
|
138
152
|
} else {
|
|
139
|
-
return scratch_context_.Get(
|
|
153
|
+
return scratch_context_.Get(executor_.isolate());
|
|
140
154
|
}
|
|
141
155
|
}
|
|
142
156
|
|
package/isolated/agent.h.cc
CHANGED
|
@@ -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&
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
},
|
package/isolated/cluster.cc
CHANGED
|
@@ -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
|
-
|
|
18
|
-
|
|
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
|
package/isolated/cluster.h.cc
CHANGED
|
@@ -8,16 +8,21 @@ namespace js::iv8::isolated {
|
|
|
8
8
|
|
|
9
9
|
export class cluster {
|
|
10
10
|
public:
|
|
11
|
-
|
|
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.
|
|
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/
|
|
9
|
-
"@auto_js/
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
package/platform/task_runner.cc
CHANGED
|
@@ -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::
|
|
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 =
|
|
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 =
|
|
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};
|
package/support/error.h.cc
CHANGED
|
@@ -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`.
|
|
44
|
-
// js::error_value
|
|
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
|
-
|
|
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
|
|
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
|
};
|
package/value/fixed_array.cc
CHANGED
|
@@ -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
|
|
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
|
|
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>;
|
package/value/primitive.cc
CHANGED
|
@@ -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::
|
|
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::
|
|
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
|
package/platform/allocator.h.cc
DELETED
|
@@ -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
|