mirror of
https://github.com/aphrody-labs/rusty_v8.git
synced 2026-10-14 12:29:01 +00:00
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).
134 lines
3.5 KiB
Rust
134 lines
3.5 KiB
Rust
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
|
|
use std::sync::atomic::AtomicU16;
|
|
|
|
struct Wrappable {
|
|
id: String,
|
|
trace_count: AtomicU16,
|
|
}
|
|
|
|
unsafe impl v8::cppgc::GarbageCollected for Wrappable {
|
|
fn trace(&self, _visitor: &mut v8::cppgc::Visitor) {
|
|
println!("Wrappable::trace() {}", self.id);
|
|
self
|
|
.trace_count
|
|
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
|
}
|
|
|
|
fn get_name(&self) -> &'static std::ffi::CStr {
|
|
c"Wrappable"
|
|
}
|
|
}
|
|
|
|
impl Drop for Wrappable {
|
|
fn drop(&mut self) {
|
|
println!("Wrappable::drop() {}", self.id);
|
|
}
|
|
}
|
|
|
|
const TAG: u16 = 1;
|
|
|
|
fn main() {
|
|
let platform = v8::new_default_platform(0, false).make_shared();
|
|
v8::V8::set_flags_from_string("--no_freeze_flags_after_init --expose-gc");
|
|
|
|
v8::V8::initialize_platform(platform.clone());
|
|
v8::cppgc::initialize_process(platform.clone());
|
|
v8::V8::initialize();
|
|
|
|
{
|
|
let heap =
|
|
v8::cppgc::Heap::create(platform, v8::cppgc::HeapCreateParams::default());
|
|
let isolate =
|
|
&mut v8::Isolate::new(v8::CreateParams::default().cpp_heap(heap));
|
|
|
|
v8::scope!(handle_scope, isolate);
|
|
let context = v8::Context::new(handle_scope, Default::default());
|
|
let scope = &mut v8::ContextScope::new(handle_scope, context);
|
|
let global = context.global(scope);
|
|
{
|
|
let func = v8::Function::new(
|
|
scope,
|
|
|scope: &mut v8::PinScope<'_, '_>,
|
|
args: v8::FunctionCallbackArguments,
|
|
mut rv: v8::ReturnValue| {
|
|
let id = args.get(0).to_rust_string_lossy(scope);
|
|
|
|
fn empty(
|
|
_scope: &mut v8::PinScope<'_, '_>,
|
|
_args: v8::FunctionCallbackArguments,
|
|
_rv: v8::ReturnValue,
|
|
) {
|
|
}
|
|
let templ = v8::FunctionTemplate::new(scope, empty);
|
|
let func = templ.get_function(scope).unwrap();
|
|
let obj = func.new_instance(scope, &[]).unwrap();
|
|
|
|
assert!(obj.is_api_wrapper());
|
|
|
|
let member = unsafe {
|
|
v8::cppgc::make_garbage_collected(
|
|
scope.get_cpp_heap().unwrap(),
|
|
Wrappable {
|
|
trace_count: AtomicU16::new(0),
|
|
id,
|
|
},
|
|
)
|
|
};
|
|
|
|
unsafe {
|
|
v8::Object::wrap::<TAG, Wrappable>(scope, obj, &member);
|
|
}
|
|
|
|
rv.set(obj.into());
|
|
},
|
|
)
|
|
.unwrap();
|
|
let name = v8::String::new(scope, "make_wrap").unwrap();
|
|
global.set(scope, name.into(), func.into()).unwrap();
|
|
}
|
|
|
|
let source = v8::String::new(
|
|
scope,
|
|
r#"
|
|
make_wrap('gc me pls'); // Inaccessible after scope.
|
|
globalThis.wrap = make_wrap('dont gc me'); // Accessible after scope.
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
execute_script(scope, source);
|
|
|
|
scope
|
|
.request_garbage_collection_for_testing(v8::GarbageCollectionType::Full);
|
|
}
|
|
|
|
// Gracefully shutdown the process.
|
|
unsafe {
|
|
v8::cppgc::shutdown_process();
|
|
v8::V8::dispose();
|
|
}
|
|
v8::V8::dispose_platform();
|
|
}
|
|
|
|
fn execute_script(
|
|
context_scope: &mut v8::ContextScope<v8::HandleScope>,
|
|
script: v8::Local<v8::String>,
|
|
) {
|
|
v8::scope!(handle_scope, context_scope);
|
|
v8::tc_scope!(let try_catch, handle_scope);
|
|
|
|
let script = v8::Script::compile(try_catch, script, None)
|
|
.expect("failed to compile script");
|
|
|
|
if script.run(try_catch).is_none() {
|
|
let exception_string = try_catch
|
|
.stack_trace()
|
|
.or_else(|| try_catch.exception())
|
|
.map_or_else(
|
|
|| "no stack trace".into(),
|
|
|value| value.to_rust_string_lossy(try_catch),
|
|
);
|
|
|
|
panic!("{exception_string}");
|
|
}
|
|
}
|