Skip to content

Commit 65fb5c1

Browse files
authored
rework scopes to make them stack allocated and remove runtime bookkeeping of scopes (#1830)
Before this change, `HandleScope`s (and other scope types) were stored in the heap and we did bookkeeping at runtime of the current stack of scopes. The reasons were primarily: - raw handle scopes need to be pinned in memory once initialized - we couldn't enforce at compile that a `Local` didn't outlive the scope it was created from The latter lead us to need a workaround where scopes actually were not exited when they were dropped, rather they were exited the next time we activated another scope. This change refactors the scope implementation to: - make scopes stack allocated, to reduce overhead of using them - remove the runtime bookkeeping - use lifetimes to prevent scopes from being dropped, so we can remove the `zombie` scope workaround The practical effect is that creating a handle scope changes from: ```rust let mut scope = v8::HandleScope::new(&mut isolate); let local = v8::Integer::new(&mut scope, 123); ``` to: ```rust let scope = std::pin::pin!(v8::HandleScope::new(&mut isolate)); let scope = scope.init(); let local = v8::Integer::new(&scope, 123); // or shorter v8::scope!(let scope, &mut isolate); let local = v8::Integer::new(scope, 123); ``` If you accept a `HandleScope` as a parameter in a function, you would change it from: ```rust fn takes_scope(s: &mut v8::HandleScope) {} ``` to ```rust fn takes_scope(s: &mut v8::PinScope) {} ``` (`v8::PinScope` is a type alias for a pinned handle scope).
1 parent 5f23df5 commit 65fb5c1

File tree

87 files changed

+4588
-3650
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+4588
-3650
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,14 @@ opt-level = 1
101101
default = ["use_custom_libcxx"]
102102
use_custom_libcxx = []
103103
v8_enable_pointer_compression = []
104+
v8_enable_v8_checks = []
104105

105106
[dependencies]
106107
bitflags = "2.5"
107108
paste = "1.0"
108-
temporal_capi = { package = "ry_temporal_capi", version = "=0.0.11-ry.1", features = [ "compiled_data" ] }
109+
temporal_capi = { package = "ry_temporal_capi", version = "=0.0.11-ry.1", features = [
110+
"compiled_data",
111+
] }
109112

110113
[build-dependencies]
111114
miniz_oxide = "0.8.8"

benches/function.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ fn main() {
1010
v8::V8::initialize_platform(platform);
1111
v8::V8::initialize();
1212
let isolate = &mut v8::Isolate::new(v8::CreateParams::default());
13-
let handle_scope = &mut v8::HandleScope::new(isolate);
13+
v8::scope!(let handle_scope, isolate);
1414
let context = v8::Context::new(handle_scope, Default::default());
1515
let scope = &mut v8::ContextScope::new(handle_scope, context);
1616
let global = context.global(scope);
1717
{
1818
let func = v8::Function::new(
1919
scope,
20-
|scope: &mut v8::HandleScope,
20+
|scope: &mut v8::PinScope,
2121
_: v8::FunctionCallbackArguments,
2222
mut rv: v8::ReturnValue| {
2323
rv.set(v8::Integer::new(scope, 42).into());
@@ -30,7 +30,8 @@ fn main() {
3030
{
3131
extern "C" fn callback(info: *const v8::FunctionCallbackInfo) {
3232
let info = unsafe { &*info };
33-
let scope = unsafe { &mut v8::CallbackScope::new(info) };
33+
let scope = std::pin::pin!(unsafe { v8::CallbackScope::new(info) });
34+
let scope = &scope.init();
3435
let mut rv = v8::ReturnValue::from_function_callback_info(info);
3536
rv.set(v8::Integer::new(scope, 42).into());
3637
}
@@ -51,7 +52,7 @@ fn main() {
5152
{
5253
let func = v8::Function::new(
5354
scope,
54-
|_: &mut v8::HandleScope,
55+
|_: &mut v8::PinScope,
5556
_: v8::FunctionCallbackArguments,
5657
mut rv: v8::ReturnValue| {
5758
rv.set_uint32(42);
@@ -74,7 +75,7 @@ fn main() {
7475
),
7576
);
7677
let template = v8::FunctionTemplate::builder(
77-
|scope: &mut v8::HandleScope,
78+
|scope: &mut v8::PinScope,
7879
_: v8::FunctionCallbackArguments,
7980
mut rv: v8::ReturnValue| {
8081
rv.set(v8::Integer::new(scope, 42).into());
@@ -90,7 +91,8 @@ fn main() {
9091
{
9192
extern "C" fn callback(info: *const v8::FunctionCallbackInfo) {
9293
let info = unsafe { &*info };
93-
let scope = unsafe { &mut v8::CallbackScope::new(info) };
94+
let scope = std::pin::pin!(unsafe { v8::CallbackScope::new(info) });
95+
let scope = &scope.init();
9496
let mut rv = v8::ReturnValue::from_function_callback_info(info);
9597
rv.set(v8::undefined(scope).into());
9698
}
@@ -156,10 +158,11 @@ fn main() {
156158
}
157159

158160
fn eval<'s>(
159-
scope: &mut v8::HandleScope<'s>,
161+
scope: &mut v8::PinScope<'s, '_>,
160162
code: &str,
161163
) -> Option<v8::Local<'s, v8::Value>> {
162-
let scope = &mut v8::EscapableHandleScope::new(scope);
164+
v8::escapable_handle_scope!(let scope, scope);
165+
163166
let source = v8::String::new(scope, code).unwrap();
164167
let script = v8::Script::compile(scope, source, None).unwrap();
165168
let r = script.run(scope);

build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ fn build_v8(is_asan: bool) {
213213
"v8_enable_pointer_compression={}",
214214
env::var("CARGO_FEATURE_V8_ENABLE_POINTER_COMPRESSION").is_ok()
215215
));
216+
gn_args.push(format!(
217+
"v8_enable_v8_checks={}",
218+
env::var("CARGO_FEATURE_V8_ENABLE_V8_CHECKS").is_ok()
219+
));
216220
// Fix GN's host_cpu detection when using x86_64 bins on Apple Silicon
217221
if cfg!(target_os = "macos") && cfg!(target_arch = "aarch64") {
218222
gn_args.push("host_cpu=\"arm64\"".to_string());

examples/cppgc-object.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,31 @@ const TAG: u16 = 1;
3030
fn main() {
3131
let platform = v8::new_default_platform(0, false).make_shared();
3232
v8::V8::set_flags_from_string("--no_freeze_flags_after_init --expose-gc");
33-
v8::V8::initialize_platform(platform.clone());
34-
v8::V8::initialize();
3533

34+
v8::V8::initialize_platform(platform.clone());
3635
v8::cppgc::initialize_process(platform.clone());
36+
v8::V8::initialize();
3737

3838
{
3939
let heap =
4040
v8::cppgc::Heap::create(platform, v8::cppgc::HeapCreateParams::default());
4141
let isolate =
4242
&mut v8::Isolate::new(v8::CreateParams::default().cpp_heap(heap));
4343

44-
let handle_scope = &mut v8::HandleScope::new(isolate);
44+
v8::scope!(handle_scope, isolate);
4545
let context = v8::Context::new(handle_scope, Default::default());
4646
let scope = &mut v8::ContextScope::new(handle_scope, context);
4747
let global = context.global(scope);
4848
{
4949
let func = v8::Function::new(
5050
scope,
51-
|scope: &mut v8::HandleScope,
51+
|scope: &mut v8::PinScope<'_, '_>,
5252
args: v8::FunctionCallbackArguments,
5353
mut rv: v8::ReturnValue| {
5454
let id = args.get(0).to_rust_string_lossy(scope);
5555

5656
fn empty(
57-
_scope: &mut v8::HandleScope,
57+
_scope: &mut v8::PinScope<'_, '_>,
5858
_args: v8::FunctionCallbackArguments,
5959
_rv: v8::ReturnValue,
6060
) {
@@ -113,8 +113,8 @@ fn execute_script(
113113
context_scope: &mut v8::ContextScope<v8::HandleScope>,
114114
script: v8::Local<v8::String>,
115115
) {
116-
let scope = &mut v8::HandleScope::new(context_scope);
117-
let try_catch = &mut v8::TryCatch::new(scope);
116+
v8::scope!(handle_scope, context_scope);
117+
v8::tc_scope!(let try_catch, handle_scope);
118118

119119
let script = v8::Script::compile(try_catch, script, None)
120120
.expect("failed to compile script");

examples/cppgc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ impl Drop for Rope {
5252
fn main() {
5353
let platform = v8::new_default_platform(0, false).make_shared();
5454
v8::V8::initialize_platform(platform.clone());
55-
v8::V8::initialize();
5655
v8::cppgc::initialize_process(platform.clone());
56+
v8::V8::initialize();
5757

5858
{
5959
// Create a managed heap.

examples/hello_world.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@ fn main() {
99
let isolate = &mut v8::Isolate::new(v8::CreateParams::default());
1010

1111
// Create a stack-allocated handle scope.
12-
let handle_scope = &mut v8::HandleScope::new(isolate);
12+
v8::scope!(let handle_scope, isolate);
1313

1414
// Create a new context.
1515
let context = v8::Context::new(handle_scope, Default::default());
1616

1717
// Enter the context for compiling and running the hello world script.
18-
let scope = &mut v8::ContextScope::new(handle_scope, context);
19-
18+
let scope = &v8::ContextScope::new(handle_scope, context);
2019
// Create a string containing the JavaScript source code.
2120
let code = v8::String::new(scope, "'Hello' + ' World!'").unwrap();
2221

examples/process.rs

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::convert::TryFrom;
33

44
#[allow(clippy::needless_pass_by_value)] // this function should follow the callback type
55
fn log_callback(
6-
scope: &mut v8::HandleScope,
6+
scope: &mut v8::PinScope,
77
args: v8::FunctionCallbackArguments,
88
mut _retval: v8::ReturnValue,
99
) {
@@ -29,13 +29,13 @@ fn main() {
2929
}
3030

3131
let mut isolate = v8::Isolate::new(v8::CreateParams::default());
32-
let mut scope = v8::HandleScope::new(&mut isolate);
32+
v8::scope!(let scope, &mut isolate);
3333

3434
let source = std::fs::read_to_string(&file)
3535
.unwrap_or_else(|err| panic!("failed to open {file}: {err}"));
36-
let source = v8::String::new(&mut scope, &source).unwrap();
36+
let source = v8::String::new(scope, &source).unwrap();
3737

38-
let mut processor = JsHttpRequestProcessor::new(&mut scope, source, options);
38+
let mut processor = JsHttpRequestProcessor::new(scope, source, options);
3939

4040
let requests = vec![
4141
StringHttpRequest::new("/process.cc", "localhost", "google.com", "firefox"),
@@ -124,22 +124,19 @@ impl HttpRequest for StringHttpRequest {
124124
}
125125

126126
/// An http request processor that is scriptable using JavaScript.
127-
struct JsHttpRequestProcessor<'s, 'i> {
128-
context: v8::Local<'s, v8::Context>,
129-
context_scope: v8::ContextScope<'i, v8::HandleScope<'s>>,
130-
process_fn: Option<v8::Local<'s, v8::Function>>,
127+
struct JsHttpRequestProcessor<'scope, 'obj, 'isolate> {
128+
context: v8::Local<'obj, v8::Context>,
129+
context_scope: v8::ContextScope<'scope, 'obj, v8::HandleScope<'isolate>>,
130+
process_fn: Option<v8::Local<'obj, v8::Function>>,
131131
request_template: v8::Global<v8::ObjectTemplate>,
132132
_map_template: Option<v8::Global<v8::ObjectTemplate>>,
133133
}
134134

135-
impl<'s, 'i> JsHttpRequestProcessor<'s, 'i>
136-
where
137-
's: 'i,
138-
{
135+
impl<'scope, 'obj, 'isolate> JsHttpRequestProcessor<'scope, 'obj, 'isolate> {
139136
/// Creates a scriptable HTTP request processor.
140137
pub fn new(
141-
isolate_scope: &'i mut v8::HandleScope<'s, ()>,
142-
source: v8::Local<'s, v8::String>,
138+
isolate_scope: &'scope mut v8::PinScope<'obj, 'isolate, ()>,
139+
source: v8::Local<'obj, v8::String>,
143140
options: HashMap<String, String>,
144141
) -> Self {
145142
let global = v8::ObjectTemplate::new(isolate_scope);
@@ -155,14 +152,13 @@ where
155152
..Default::default()
156153
},
157154
);
158-
let mut context_scope = v8::ContextScope::new(isolate_scope, context);
155+
let context_scope = v8::ContextScope::new(isolate_scope, context);
159156

160-
let request_template = v8::ObjectTemplate::new(&mut context_scope);
157+
let request_template = v8::ObjectTemplate::new(&context_scope);
161158
request_template.set_internal_field_count(1);
162159

163160
// make it global
164-
let request_template =
165-
v8::Global::new(&mut context_scope, request_template);
161+
let request_template = v8::Global::new(&context_scope, request_template);
166162

167163
let mut self_ = JsHttpRequestProcessor {
168164
context,
@@ -174,32 +170,29 @@ where
174170

175171
// loads options and output
176172
let options = self_.wrap_map(options);
177-
let options_str =
178-
v8::String::new(&mut self_.context_scope, "options").unwrap();
179-
self_.context.global(&mut self_.context_scope).set(
180-
&mut self_.context_scope,
173+
let options_str = v8::String::new(&self_.context_scope, "options").unwrap();
174+
self_.context.global(&self_.context_scope).set(
175+
&self_.context_scope,
181176
options_str.into(),
182177
options.into(),
183178
);
184179

185-
let output = v8::Object::new(&mut self_.context_scope);
186-
let output_str =
187-
v8::String::new(&mut self_.context_scope, "output").unwrap();
188-
self_.context.global(&mut self_.context_scope).set(
189-
&mut self_.context_scope,
180+
let output = v8::Object::new(&self_.context_scope);
181+
let output_str = v8::String::new(&self_.context_scope, "output").unwrap();
182+
self_.context.global(&self_.context_scope).set(
183+
&self_.context_scope,
190184
output_str.into(),
191185
output.into(),
192186
);
193187

194188
// execute script
195189
self_.execute_script(source);
196190

197-
let process_str =
198-
v8::String::new(&mut self_.context_scope, "Process").unwrap();
191+
let process_str = v8::String::new(&self_.context_scope, "Process").unwrap();
199192
let process_fn = self_
200193
.context
201-
.global(&mut self_.context_scope)
202-
.get(&mut self_.context_scope, process_str.into())
194+
.global(&self_.context_scope)
195+
.get(&self_.context_scope, process_str.into())
203196
.expect("missing function Process");
204197

205198
let process_fn = v8::Local::<v8::Function>::try_from(process_fn)
@@ -209,9 +202,10 @@ where
209202
self_
210203
}
211204

212-
fn execute_script(&mut self, script: v8::Local<'s, v8::String>) {
213-
let scope = &mut v8::HandleScope::new(&mut self.context_scope);
214-
let try_catch = &mut v8::TryCatch::new(scope);
205+
fn execute_script(&mut self, script: v8::Local<'scope, v8::String>) {
206+
v8::scope!(let scope, &mut self.context_scope);
207+
208+
v8::tc_scope!(let try_catch, scope);
215209

216210
let script = v8::Script::compile(try_catch, script, None)
217211
.expect("failed to compile script");
@@ -235,8 +229,9 @@ where
235229
let request: Box<dyn HttpRequest> = Box::new(request);
236230
let request = self.wrap_request(request);
237231

238-
let scope = &mut v8::HandleScope::new(&mut self.context_scope);
239-
let try_catch = &mut v8::TryCatch::new(scope);
232+
v8::scope!(let scope, &mut self.context_scope);
233+
234+
v8::tc_scope!(let try_catch, scope);
240235

241236
let process_fn = self.process_fn.as_mut().unwrap();
242237
let global = self.context.global(try_catch).into();
@@ -259,7 +254,7 @@ where
259254
fn wrap_request(
260255
&mut self,
261256
request: Box<dyn HttpRequest>,
262-
) -> v8::Local<'s, v8::Object> {
257+
) -> v8::Local<'scope, v8::Object> {
263258
// TODO: fix memory leak
264259

265260
use std::ffi::c_void;
@@ -295,7 +290,7 @@ where
295290
/// This handles the properties of `HttpRequest`
296291
#[allow(clippy::needless_pass_by_value)] // this function should follow the callback type
297292
fn request_prop_handler(
298-
scope: &mut v8::HandleScope,
293+
scope: &mut v8::PinScope,
299294
key: v8::Local<v8::Name>,
300295
args: v8::PropertyCallbackArguments,
301296
mut rv: v8::ReturnValue,
@@ -327,7 +322,7 @@ where
327322

328323
/// Utility function that extracts the http request object from a wrapper object.
329324
fn unwrap_request(
330-
scope: &mut v8::HandleScope,
325+
scope: &v8::PinScope,
331326
request: v8::Local<v8::Object>,
332327
) -> *mut Box<dyn HttpRequest> {
333328
let external = request
@@ -340,9 +335,9 @@ where
340335
fn wrap_map(
341336
&mut self,
342337
options: HashMap<String, String>,
343-
) -> v8::Local<'s, v8::Object> {
338+
) -> v8::Local<'scope, v8::Object> {
344339
// TODO: wrap map, not convert into Object
345-
let scope = &mut self.context_scope;
340+
let scope = &self.context_scope;
346341
let result = v8::Object::new(scope);
347342

348343
for (key, value) in options {
@@ -356,7 +351,9 @@ where
356351

357352
/// Prints the output.
358353
pub fn print_output(&mut self) {
359-
let scope = &mut v8::HandleScope::new(&mut self.context_scope);
354+
let scope: std::pin::Pin<&mut v8::ScopeStorage<v8::HandleScope<'_>>> =
355+
std::pin::pin!(v8::HandleScope::new(&mut self.context_scope));
356+
let scope = &scope.init();
360357
let key = v8::String::new(scope, "output").unwrap();
361358
let output = self
362359
.context

0 commit comments

Comments
 (0)