@auto_js/v8 0.0.8 → 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/CMakeLists.txt +2 -0
- package/_module.cc +2 -0
- package/evaluation/module.cc +23 -20
- package/evaluation/module.h.cc +16 -17
- package/evaluation/script.cc +7 -7
- package/evaluation/script.h.cc +5 -8
- package/handle/value_of.cc +7 -3
- package/isolated/agent.cc +9 -5
- package/isolated/agent.h.cc +6 -0
- package/isolated/cluster.h.cc +1 -1
- package/isolated/memory_policy.cc +49 -0
- package/isolated/memory_policy.h.cc +48 -0
- package/package.json +3 -3
- package/platform/delegate.cc +1 -1
- package/platform/delegate.h.cc +1 -1
- package/platform/foreground_runner.cc +2 -2
- package/platform/scheduler.cc +2 -2
- package/platform/task.cc +1 -1
- package/support/callback.cc +3 -3
- package/support/callback_storage.cc +2 -2
- package/support/error.h.cc +1 -1
- package/transfer/accept.cc +2 -2
- package/transfer/accept.h.cc +105 -40
- package/transfer/visit.cc +61 -7
- package/value/function.cc +4 -2
package/CMakeLists.txt
CHANGED
|
@@ -64,6 +64,7 @@ if(v8_js STREQUAL isolated_v8_js)
|
|
|
64
64
|
isolated/clock.cc
|
|
65
65
|
isolated/cluster.cc
|
|
66
66
|
isolated/executor.cc
|
|
67
|
+
isolated/memory_policy.cc
|
|
67
68
|
isolated/platform.cc
|
|
68
69
|
platform/delegate.cc
|
|
69
70
|
platform/foreground_runner.cc
|
|
@@ -76,6 +77,7 @@ if(v8_js STREQUAL isolated_v8_js)
|
|
|
76
77
|
isolated/clock.h.cc
|
|
77
78
|
isolated/cluster.h.cc
|
|
78
79
|
isolated/executor.h.cc
|
|
80
|
+
isolated/memory_policy.h.cc
|
|
79
81
|
isolated/platform.h.cc
|
|
80
82
|
platform/delegate.h.cc
|
|
81
83
|
platform/foreground_runner.h.cc
|
package/_module.cc
CHANGED
|
@@ -24,6 +24,8 @@ 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;
|
|
28
|
+
export import :isolated.memory_policy;
|
|
27
29
|
export import :isolated.platform;
|
|
28
30
|
export import :platform.delegate;
|
|
29
31
|
export import :platform.foreground_runner;
|
package/evaluation/module.cc
CHANGED
|
@@ -11,8 +11,8 @@ import v8;
|
|
|
11
11
|
|
|
12
12
|
namespace js::iv8 {
|
|
13
13
|
|
|
14
|
-
auto module_record::requests(context_lock_witness lock
|
|
15
|
-
auto requests_array = fixed_array{lock.context(),
|
|
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) ->
|
|
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
|
|
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<
|
|
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 = [ & ]
|
|
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
|
|
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(
|
|
108
|
+
unmaybe(synthetic_module->InstantiateModule(lock.context(), null_callback));
|
|
111
109
|
|
|
112
110
|
// `Evaluate` invokes `evaluation_steps` above
|
|
113
|
-
unmaybe(
|
|
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,
|
|
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
|
-
|
|
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
|
|
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(
|
|
164
|
+
unmaybe(InstantiateModule(lock.context(), v8_callback));
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
auto module_record::evaluate(context_lock_witness lock
|
|
168
|
-
auto promise =
|
|
169
|
-
|
|
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) {
|
package/evaluation/module.h.cc
CHANGED
|
@@ -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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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) ->
|
|
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<
|
|
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<
|
|
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<
|
|
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<
|
|
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) ->
|
|
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) ->
|
|
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<
|
|
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<
|
|
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<
|
|
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());
|
package/evaluation/script.cc
CHANGED
|
@@ -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) ->
|
|
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
|
-
|
|
19
|
-
|
|
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
|
|
24
|
-
auto
|
|
25
|
-
return unmaybe(
|
|
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
|
package/evaluation/script.h.cc
CHANGED
|
@@ -7,23 +7,20 @@ import v8;
|
|
|
7
7
|
|
|
8
8
|
namespace js::iv8 {
|
|
9
9
|
|
|
10
|
-
export
|
|
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) ->
|
|
18
|
-
|
|
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) ->
|
|
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) ->
|
|
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
|
}
|
package/handle/value_of.cc
CHANGED
|
@@ -9,9 +9,9 @@ namespace js::iv8 {
|
|
|
9
9
|
template <class Tag>
|
|
10
10
|
struct value_specialization;
|
|
11
11
|
|
|
12
|
-
// nb: This corresponds to `js::napi::
|
|
13
|
-
// `js::napi::
|
|
14
|
-
template <class Tag>
|
|
12
|
+
// nb: This corresponds to `js::napi::value_of<T>`. `v8::Local<T>` is more like
|
|
13
|
+
// `js::napi::local_of<T>`.
|
|
14
|
+
export template <class Tag>
|
|
15
15
|
class value_of : public value_specialization<Tag>::type {
|
|
16
16
|
public:
|
|
17
17
|
using value_specialization<Tag>::type::type;
|
|
@@ -50,9 +50,13 @@ struct value_specialization<date_tag> : std::type_identity<class value_for_date>
|
|
|
50
50
|
template <>
|
|
51
51
|
struct value_specialization<external_tag> : std::type_identity<class value_for_external> {};
|
|
52
52
|
|
|
53
|
+
// TODO: value_for_record
|
|
53
54
|
template <>
|
|
54
55
|
struct value_specialization<object_tag> : std::type_identity<class value_for_object> {};
|
|
55
56
|
|
|
57
|
+
template <>
|
|
58
|
+
struct value_specialization<list_tag> : std::type_identity<class value_for_object> {};
|
|
59
|
+
|
|
56
60
|
template <>
|
|
57
61
|
struct value_specialization<array_buffer_tag> : std::type_identity<class value_for_array_buffer> {};
|
|
58
62
|
|
package/isolated/agent.cc
CHANGED
|
@@ -75,18 +75,22 @@ agent_host::agent_host(
|
|
|
75
75
|
behavior_params params
|
|
76
76
|
) :
|
|
77
77
|
storage_{std::move(storage)},
|
|
78
|
+
memory_policy_{std::move(params.memory_policy)},
|
|
78
79
|
clock_{params.clock},
|
|
79
80
|
destroy_callback_{destroy_callback},
|
|
80
81
|
reset_handle_callback_{reset_handle_type{util::fn<&agent_host::remote_expiration_callback>, *this}},
|
|
81
82
|
random_seed_{params.random_seed} {
|
|
82
83
|
executor_.initialize([ & ](v8::Isolate* isolate) noexcept -> auto {
|
|
83
84
|
isolate->SetData(0, this);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
v8::Isolate::CreateParams params;
|
|
86
|
+
auto memory_params = memory_policy_->make_create_params();
|
|
87
|
+
params.constraints = memory_params.constraints;
|
|
88
|
+
params.array_buffer_allocator_shared = std::move(memory_params.allocator);
|
|
89
|
+
return params;
|
|
88
90
|
});
|
|
89
|
-
executor_.isolate()
|
|
91
|
+
auto* isolate = executor_.isolate();
|
|
92
|
+
isolate->SetCaptureStackTraceForUncaughtExceptions(true);
|
|
93
|
+
memory_policy_->initialize(isolate);
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
agent_host::~agent_host() {
|
package/isolated/agent.h.cc
CHANGED
|
@@ -4,6 +4,7 @@ export module v8_js:isolated.agent;
|
|
|
4
4
|
import :collected_handle;
|
|
5
5
|
import :isolated.clock;
|
|
6
6
|
import :isolated.executor;
|
|
7
|
+
import :isolated.memory_policy;
|
|
7
8
|
import :platform.foreground_runner;
|
|
8
9
|
import :remote;
|
|
9
10
|
import std;
|
|
@@ -18,6 +19,7 @@ class agent_host_of;
|
|
|
18
19
|
|
|
19
20
|
// `agent_host` behavior parameters
|
|
20
21
|
export struct behavior_params {
|
|
22
|
+
isolated::memory_policy::covariant memory_policy;
|
|
21
23
|
clock::any_clock clock;
|
|
22
24
|
std::optional<double> random_seed;
|
|
23
25
|
};
|
|
@@ -146,6 +148,7 @@ export class agent_host
|
|
|
146
148
|
// Order matters for these members
|
|
147
149
|
std::shared_ptr<agent_storage> storage_;
|
|
148
150
|
isolated::executor executor_;
|
|
151
|
+
isolated::memory_policy::covariant memory_policy_;
|
|
149
152
|
|
|
150
153
|
// Order doesn't really matter
|
|
151
154
|
bool should_give_seed_{};
|
|
@@ -198,6 +201,9 @@ auto agent_handle_of<Type>::schedule(Task task, Args... args) const -> void {
|
|
|
198
201
|
[](std::stop_token /*stop_token*/, const auto& host, const auto& task, auto&&... args) -> void {
|
|
199
202
|
auto isolate_lock = isolate_execution_lock{host->executor().isolate()};
|
|
200
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};
|
|
201
207
|
task(lock{isolate_lock, static_cast<agent_host_of<Type>&>(*host)}, std::forward<decltype(args)>(args)...);
|
|
202
208
|
},
|
|
203
209
|
std::move(host),
|
package/isolated/cluster.h.cc
CHANGED
|
@@ -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{[ & ]
|
|
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};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module v8_js;
|
|
2
|
+
import :isolated.memory_policy;
|
|
3
|
+
|
|
4
|
+
namespace js::iv8::isolated {
|
|
5
|
+
|
|
6
|
+
// memory_policy::none
|
|
7
|
+
auto memory_policy::none::make_create_params() -> create_params {
|
|
8
|
+
return {
|
|
9
|
+
.allocator = std::shared_ptr<v8::ArrayBuffer::Allocator>(v8::ArrayBuffer::Allocator::NewDefaultAllocator()),
|
|
10
|
+
.constraints = {}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// memory_policy::limited
|
|
15
|
+
auto memory_policy::limited::initialize(v8::Isolate* isolate) -> void {
|
|
16
|
+
isolate->AddNearHeapLimitCallback(
|
|
17
|
+
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
|
|
18
|
+
[](void* data, std::size_t current_heap_limit, std::size_t initial_heap_limit) -> std::size_t {
|
|
19
|
+
auto& self = *static_cast<memory_policy::limited*>(data);
|
|
20
|
+
return self.near_heap_limit_callback(current_heap_limit, initial_heap_limit);
|
|
21
|
+
},
|
|
22
|
+
this
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
auto memory_policy::limited::make_create_params() -> create_params {
|
|
27
|
+
v8::ResourceConstraints constraints{};
|
|
28
|
+
constraints.ConfigureDefaultsFromHeapSize(0, memory_limit_);
|
|
29
|
+
return {
|
|
30
|
+
.allocator = std::shared_ptr<v8::ArrayBuffer::Allocator>(v8::ArrayBuffer::Allocator::NewDefaultAllocator()),
|
|
31
|
+
.constraints = constraints,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
|
|
36
|
+
auto memory_policy::limited::near_heap_limit_callback(std::size_t current_heap_limit, std::size_t /*initial_heap_limit*/) const -> std::size_t {
|
|
37
|
+
auto* isolate = v8::Isolate::GetCurrent();
|
|
38
|
+
v8::HeapStatistics heap;
|
|
39
|
+
isolate->GetHeapStatistics(&heap);
|
|
40
|
+
|
|
41
|
+
if (current_heap_limit > memory_limit_) {
|
|
42
|
+
auto& executor = agent_host::get_current(isolate).executor();
|
|
43
|
+
executor.terminate();
|
|
44
|
+
}
|
|
45
|
+
// Increase heap limit 80mb at a time. Experimentally derived.
|
|
46
|
+
return current_heap_limit + (80 << 20);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
} // namespace js::iv8::isolated
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export module v8_js:isolated.memory_policy;
|
|
2
|
+
import std;
|
|
3
|
+
import util;
|
|
4
|
+
import v8;
|
|
5
|
+
|
|
6
|
+
namespace js::iv8::isolated {
|
|
7
|
+
|
|
8
|
+
export class memory_policy {
|
|
9
|
+
protected:
|
|
10
|
+
~memory_policy() = default;
|
|
11
|
+
|
|
12
|
+
public:
|
|
13
|
+
struct create_params;
|
|
14
|
+
|
|
15
|
+
// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor)
|
|
16
|
+
class none;
|
|
17
|
+
// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor)
|
|
18
|
+
class limited;
|
|
19
|
+
|
|
20
|
+
virtual auto initialize(v8::Isolate* isolate) -> void = 0;
|
|
21
|
+
virtual auto make_create_params() -> create_params = 0;
|
|
22
|
+
// virtual auto check_soft_limit(v8::Isolate* isolate) -> bool = 0;
|
|
23
|
+
using covariant = util::covariant_value<memory_policy, none, limited>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
struct memory_policy::create_params {
|
|
27
|
+
std::shared_ptr<v8::ArrayBuffer::Allocator> allocator;
|
|
28
|
+
v8::ResourceConstraints constraints;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
class memory_policy::none final : public memory_policy {
|
|
32
|
+
public:
|
|
33
|
+
auto initialize(v8::Isolate* isolate) -> void override {};
|
|
34
|
+
auto make_create_params() -> create_params override;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
class memory_policy::limited final : public memory_policy {
|
|
38
|
+
public:
|
|
39
|
+
explicit limited(std::size_t memory_limit_bytes) : memory_limit_{memory_limit_bytes} {}
|
|
40
|
+
auto initialize(v8::Isolate* isolate) -> void override;
|
|
41
|
+
auto make_create_params() -> create_params override;
|
|
42
|
+
|
|
43
|
+
private:
|
|
44
|
+
std::size_t memory_limit_;
|
|
45
|
+
[[nodiscard]] auto near_heap_limit_callback(std::size_t current_heap_limit, std::size_t initial_heap_limit) const -> std::size_t;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
} // 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.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/
|
|
9
|
-
"@auto_js/
|
|
8
|
+
"@auto_js/js": "^0.0.10",
|
|
9
|
+
"@auto_js/v8_exports": "^0.0.8"
|
|
10
10
|
},
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
package/platform/delegate.cc
CHANGED
|
@@ -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{[ & ]
|
|
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) {
|
package/platform/delegate.h.cc
CHANGED
|
@@ -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 = []
|
|
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::
|
|
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
|
-
[]
|
|
44
|
+
[] -> std::shared_ptr<v8::TaskRunner> {
|
|
45
45
|
throw std::logic_error{"Invalid 'foreground_runner' priority"};
|
|
46
46
|
},
|
|
47
47
|
}
|
package/platform/scheduler.cc
CHANGED
|
@@ -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 ] = [ & ]
|
|
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 ] = [ & ]
|
|
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 = [ & ]
|
|
46
|
+
auto task = [ & ] -> auto {
|
|
47
47
|
if constexpr (sizeof...(Args) == 0) {
|
|
48
48
|
return std::move(callback);
|
|
49
49
|
} else {
|
package/support/callback.cc
CHANGED
|
@@ -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, [ & ]
|
|
33
|
-
auto run = util::regular_return{[ & ]
|
|
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{[ & ]
|
|
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 = [ & ]
|
|
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 = [ & ]
|
|
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));
|
package/support/error.h.cc
CHANGED
|
@@ -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 {
|
package/transfer/accept.cc
CHANGED
|
@@ -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>{[ & ]
|
|
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 = [ & ]
|
|
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.
|
package/transfer/accept.h.cc
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export module v8_js:accept;
|
|
2
2
|
import :callback_storage;
|
|
3
|
+
import :handle.value;
|
|
3
4
|
import :hash;
|
|
4
5
|
import :value;
|
|
5
6
|
import auto_js;
|
|
@@ -15,10 +16,10 @@ constexpr auto make_free_function(auto function);
|
|
|
15
16
|
|
|
16
17
|
// Reference acceptor
|
|
17
18
|
struct reaccept_v8_value {
|
|
18
|
-
using reference_type = v8::Local<v8::
|
|
19
|
+
using reference_type = v8::Local<v8::Data>;
|
|
19
20
|
|
|
20
21
|
template <class Type>
|
|
21
|
-
constexpr auto operator()(std::type_identity<v8::Local<Type>> /*type*/, v8::Local<v8::
|
|
22
|
+
constexpr auto operator()(std::type_identity<v8::Local<Type>> /*type*/, v8::Local<v8::Data> subject) const -> v8::Local<Type> {
|
|
22
23
|
return subject.As<Type>();
|
|
23
24
|
}
|
|
24
25
|
};
|
|
@@ -62,6 +63,12 @@ struct accept_v8_primitive {
|
|
|
62
63
|
return (*this)(number_tag_of<double>{}, visit, double{std::forward<decltype(subject)>(subject)});
|
|
63
64
|
}
|
|
64
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
|
+
|
|
65
72
|
template <class Type>
|
|
66
73
|
auto operator()(number_tag_of<Type> tag, visit_holder visit, auto&& subject) const -> v8::Local<tag_to_v8<number_tag_of<Type>>> {
|
|
67
74
|
return (*this)(tag, visit, Type{std::forward<decltype(subject)>(subject)});
|
|
@@ -166,12 +173,10 @@ struct accept_v8_value : accept_v8_primitive {
|
|
|
166
173
|
[](v8::Local<v8::Array> array, auto& self, auto& visit, auto /*&&*/ subject) -> void {
|
|
167
174
|
auto&& range = util::into_range(std::forward<decltype(subject)>(subject));
|
|
168
175
|
for (auto&& [ key, value ] : util::forward_range(std::forward<decltype(range)>(range))) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
);
|
|
174
|
-
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));
|
|
175
180
|
}
|
|
176
181
|
},
|
|
177
182
|
};
|
|
@@ -202,8 +207,8 @@ struct accept_v8_value : accept_v8_primitive {
|
|
|
202
207
|
v8::Array::New(self.isolate(), Size),
|
|
203
208
|
std::forward_as_tuple(self, visit, std::forward<decltype(subject)>(subject)),
|
|
204
209
|
[](v8::Local<v8::Array> array, auto& self, auto& visit, auto /*&&*/ tuple) -> void {
|
|
205
|
-
|
|
206
|
-
(..., [ & ]
|
|
210
|
+
constexpr auto [... indices ] = util::sequence<Size>;
|
|
211
|
+
(..., [ & ] -> void {
|
|
207
212
|
auto element = visit(indices, std::forward<decltype(tuple)>(tuple), self);
|
|
208
213
|
unmaybe(array->Set(self.context_, indices, element));
|
|
209
214
|
}());
|
|
@@ -220,12 +225,10 @@ struct accept_v8_value : accept_v8_primitive {
|
|
|
220
225
|
[](v8::Local<v8::Object> object, auto& self, auto& visit, auto /*&&*/ subject) -> void {
|
|
221
226
|
auto&& range = util::into_range(std::forward<decltype(subject)>(subject));
|
|
222
227
|
for (auto&& [ key, value ] : util::forward_range(std::forward<decltype(range)>(range))) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
);
|
|
228
|
-
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));
|
|
229
232
|
}
|
|
230
233
|
},
|
|
231
234
|
};
|
|
@@ -280,8 +283,8 @@ struct accept_v8_value : accept_v8_primitive {
|
|
|
280
283
|
|
|
281
284
|
template <std::size_t Size>
|
|
282
285
|
auto accept_entry_pair_struct(this const auto& self, auto& visit, v8::Local<v8::Object> target, auto&& subject) -> void {
|
|
283
|
-
|
|
284
|
-
(..., [ & ]
|
|
286
|
+
constexpr auto [... indices ] = util::sequence<Size>;
|
|
287
|
+
(..., [ & ] -> void {
|
|
285
288
|
auto accept_entry = accept_entry_pair<decltype(self), decltype(self)>{self};
|
|
286
289
|
auto entry = visit(std::integral_constant<std::size_t, indices>{}, std::forward<decltype(subject)>(subject), accept_entry);
|
|
287
290
|
unmaybe(target->Set(self.context_, entry.first, entry.second));
|
|
@@ -292,44 +295,56 @@ struct accept_v8_value : accept_v8_primitive {
|
|
|
292
295
|
v8::Local<v8::Context> context_;
|
|
293
296
|
};
|
|
294
297
|
|
|
295
|
-
//
|
|
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.
|
|
296
300
|
template <class Lock>
|
|
297
|
-
struct
|
|
301
|
+
struct accept_v8_value_lock_delegate;
|
|
298
302
|
|
|
299
303
|
template <>
|
|
300
|
-
struct
|
|
304
|
+
struct accept_v8_value_lock_delegate<context_lock_witness> : accept_v8_value {
|
|
301
305
|
using accept_v8_value::accept_v8_value;
|
|
302
|
-
using accept_v8_value::operator();
|
|
303
|
-
using accept_v8_value::witness;
|
|
304
|
-
|
|
305
|
-
// function instantiation
|
|
306
|
-
auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, auto subject) const -> v8::Local<iv8::Function> {
|
|
307
|
-
auto [ function, length ] = make_free_function<context_lock_witness>(std::move(subject).callback);
|
|
308
|
-
auto [ callback, data ] = make_callback_storage(witness(), std::move(function));
|
|
309
|
-
return unmaybe(v8::Function::New(witness().context(), callback, data, length, v8::ConstructorBehavior::kThrow).template As<iv8::Function>());
|
|
310
|
-
}
|
|
311
306
|
};
|
|
312
307
|
|
|
313
308
|
template <class Lock>
|
|
314
|
-
struct
|
|
309
|
+
struct accept_v8_value_lock_delegate : accept_v8_value {
|
|
315
310
|
public:
|
|
316
|
-
|
|
311
|
+
accept_v8_value_lock_delegate(auto* transfer, const Lock& lock) :
|
|
317
312
|
accept_v8_value{transfer, lock},
|
|
318
313
|
lock_{lock} {}
|
|
319
314
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
// function instantiation
|
|
323
|
-
auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, auto subject) const -> v8::Local<iv8::Function> {
|
|
324
|
-
auto [ function, length ] = make_free_function<Lock>(std::move(subject).callback);
|
|
325
|
-
auto [ callback, data ] = make_callback_storage(lock_.get(), std::move(function));
|
|
326
|
-
return v8::Function::New(lock_.get().context(), callback, data, length, v8::ConstructorBehavior::kThrow);
|
|
327
|
-
}
|
|
315
|
+
[[nodiscard]] auto witness() const -> const Lock& { return lock_; }
|
|
328
316
|
|
|
329
317
|
private:
|
|
330
318
|
std::reference_wrapper<const Lock> lock_;
|
|
331
319
|
};
|
|
332
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
|
+
|
|
333
348
|
// Acceptor with template environment
|
|
334
349
|
template <class Meta, class Lock>
|
|
335
350
|
struct accept_v8_template : accept_v8_primitive {
|
|
@@ -338,6 +353,36 @@ struct accept_v8_template : accept_v8_primitive {
|
|
|
338
353
|
accept_v8_primitive{transfer, lock},
|
|
339
354
|
lock_{lock} {}
|
|
340
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
|
+
|
|
341
386
|
// function template
|
|
342
387
|
auto operator()(function_prototype_tag /*tag*/, visit_holder /*visit*/, auto subject) const -> v8::Local<v8::FunctionTemplate> {
|
|
343
388
|
auto [ function, length ] = make_free_function<Lock>(std::move(subject).callback);
|
|
@@ -370,6 +415,20 @@ struct accept_v8_property_value {
|
|
|
370
415
|
accept_value<Meta, Type> second;
|
|
371
416
|
};
|
|
372
417
|
|
|
418
|
+
// Forward `value_of<T>`
|
|
419
|
+
template <class Tag>
|
|
420
|
+
struct accept_v8_value_of {
|
|
421
|
+
public:
|
|
422
|
+
explicit accept_v8_value_of(auto* /*transfer*/) {}
|
|
423
|
+
|
|
424
|
+
auto operator()(Tag /*tag*/, visit_holder /*visit*/, value_of<Tag> subject) const -> value_of<Tag> {
|
|
425
|
+
return subject;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
consteval static auto accept_tags_of() { return std::tuple{Tag{}}; }
|
|
429
|
+
consteval static auto types(auto /*recursive*/) { return util::type_pack{}; }
|
|
430
|
+
};
|
|
431
|
+
|
|
373
432
|
// `return_into(...)`
|
|
374
433
|
struct return_into_marker {};
|
|
375
434
|
|
|
@@ -475,6 +534,12 @@ struct accept<Meta, v8::Local<Type>> : iv8::accept_v8_value_with<typename Meta::
|
|
|
475
534
|
using iv8::accept_v8_value_with<typename Meta::accept_context_type>::accept_v8_value_with;
|
|
476
535
|
};
|
|
477
536
|
|
|
537
|
+
// forward `value_of<T>`
|
|
538
|
+
template <class Meta, class Tag>
|
|
539
|
+
struct accept<Meta, iv8::value_of<Tag>> : iv8::accept_v8_value_of<Tag> {
|
|
540
|
+
using iv8::accept_v8_value_of<Tag>::accept_v8_value_of;
|
|
541
|
+
};
|
|
542
|
+
|
|
478
543
|
// templates
|
|
479
544
|
template <class Meta, class Type>
|
|
480
545
|
requires std::is_convertible_v<Type, v8::Template>
|
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, [ & ]
|
|
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, [ & ]
|
|
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, [ & ]
|
|
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
|
-
[ & ]
|
|
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, [ & ]
|
|
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
|
-
[ & ]
|
|
340
|
+
[ & ] -> accept_target_t<Accept> {
|
|
341
341
|
return immediate(subject, accept);
|
|
342
342
|
}
|
|
343
343
|
}
|
|
@@ -380,7 +380,7 @@ struct visit_value : visit_flat_value<Target> {
|
|
|
380
380
|
template <class Accept>
|
|
381
381
|
auto immediate(v8::Local<v8::Array> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
382
382
|
auto visit_entry = visit_entry_pair<visit_property_name<visit_value>, visit_value&>{*this};
|
|
383
|
-
return accept(list_tag{}, visit_entry, value_of{witness(), subject.As<v8::
|
|
383
|
+
return accept(list_tag{}, visit_entry, value_of{witness(), subject.As<v8::Array>()});
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
// function template
|
|
@@ -433,10 +433,55 @@ struct visit_value : visit_flat_value<Target> {
|
|
|
433
433
|
v8::Local<v8::Context> context_;
|
|
434
434
|
};
|
|
435
435
|
|
|
436
|
+
// Forward `value_of<T>` back to acceptor
|
|
437
|
+
template <class Tag>
|
|
438
|
+
struct visit_v8_value_of {
|
|
439
|
+
private:
|
|
440
|
+
using visit_type = visit_value<void>;
|
|
441
|
+
|
|
442
|
+
public:
|
|
443
|
+
constexpr explicit visit_v8_value_of(auto* transfer, context_lock_witness lock) : visit_{transfer, lock} {}
|
|
444
|
+
|
|
445
|
+
template <class Accept>
|
|
446
|
+
constexpr auto operator()(value_of<Tag> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
447
|
+
return accept(Tag{}, visit_, subject);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
template <class Accept>
|
|
451
|
+
requires std::is_same_v<Tag, object_tag>
|
|
452
|
+
constexpr auto operator()(value_of<object_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
453
|
+
auto visit_entry = visit_entry_pair<visit_property_name<visit_type>, visit_type&>{visit_};
|
|
454
|
+
return accept(object_tag{}, visit_entry, subject);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
template <class Accept>
|
|
458
|
+
requires std::is_same_v<Tag, list_tag>
|
|
459
|
+
constexpr auto operator()(value_of<list_tag> subject, const Accept& accept) -> accept_target_t<Accept> {
|
|
460
|
+
auto visit_entry = visit_entry_pair<visit_property_name<visit_type>, visit_type&>{visit_};
|
|
461
|
+
return accept(list_tag{}, visit_entry, subject);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
consteval static auto types(auto /*recursive*/) { return util::type_pack{}; }
|
|
465
|
+
|
|
466
|
+
private:
|
|
467
|
+
visit_type visit_;
|
|
468
|
+
};
|
|
469
|
+
|
|
436
470
|
// Template visitor needs no lock. The acceptor will instantiate values.
|
|
437
471
|
struct visit_template {
|
|
438
472
|
explicit visit_template(auto* /*transfer*/) {}
|
|
439
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
|
+
|
|
440
485
|
template <class Accept>
|
|
441
486
|
auto operator()(v8::Local<v8::FunctionTemplate> subject, const Accept& accept) const -> accept_target_t<Accept> {
|
|
442
487
|
return accept(function_prototype_tag{}, *this, subject);
|
|
@@ -467,6 +512,15 @@ struct visit<Meta, v8::Local<Type>> : iv8::visit_value_with<Meta> {
|
|
|
467
512
|
using iv8::visit_value_with<Meta>::visit_value_with;
|
|
468
513
|
};
|
|
469
514
|
|
|
515
|
+
// value_of<T> visitor
|
|
516
|
+
template <class Meta, class Tag>
|
|
517
|
+
struct visit<Meta, iv8::value_of<Tag>> : iv8::visit_v8_value_of<Tag> {
|
|
518
|
+
using iv8::visit_v8_value_of<Tag>::visit_v8_value_of;
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
template <class Tag>
|
|
522
|
+
struct visit_subject_for<iv8::value_of<Tag>> : std::type_identity<v8::Local<v8::Value>> {};
|
|
523
|
+
|
|
470
524
|
// Template visitor
|
|
471
525
|
template <class Meta, class Type>
|
|
472
526
|
requires std::is_convertible_v<Type, v8::Template>
|
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);
|