mirror of
https://github.com/denoland/rusty_v8.git
synced 2024-11-21 20:28:58 +00:00
200 lines
5.1 KiB
Rust
200 lines
5.1 KiB
Rust
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
use v8::cppgc::{GarbageCollected, Visitor};
|
|
|
|
struct CppGCGuard {
|
|
pub platform: v8::SharedRef<v8::Platform>,
|
|
}
|
|
|
|
fn initalize_test() -> CppGCGuard {
|
|
v8::V8::set_flags_from_string("--no_freeze_flags_after_init --expose-gc");
|
|
let platform = v8::new_unprotected_default_platform(0, false).make_shared();
|
|
v8::V8::initialize_platform(platform.clone());
|
|
v8::V8::initialize();
|
|
v8::cppgc::initalize_process(platform.clone());
|
|
|
|
CppGCGuard { platform }
|
|
}
|
|
|
|
impl Drop for CppGCGuard {
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
v8::cppgc::shutdown_process();
|
|
v8::V8::dispose();
|
|
}
|
|
v8::V8::dispose_platform();
|
|
}
|
|
}
|
|
|
|
const TAG: u16 = 1;
|
|
|
|
#[test]
|
|
fn cppgc_object_wrap() {
|
|
let guard = initalize_test();
|
|
|
|
static TRACE_COUNT: AtomicUsize = AtomicUsize::new(0);
|
|
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
struct Wrap {
|
|
value: v8::TracedReference<v8::Value>,
|
|
}
|
|
|
|
impl GarbageCollected for Wrap {
|
|
fn trace(&self, visitor: &Visitor) {
|
|
TRACE_COUNT.fetch_add(1, Ordering::SeqCst);
|
|
visitor.trace(&self.value);
|
|
}
|
|
|
|
fn get_name(&self) -> Option<&'static std::ffi::CStr> {
|
|
Some(c"Eyecatcher")
|
|
}
|
|
}
|
|
|
|
impl Drop for Wrap {
|
|
fn drop(&mut self) {
|
|
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
|
|
}
|
|
}
|
|
|
|
fn op_wrap(
|
|
scope: &mut v8::HandleScope,
|
|
args: v8::FunctionCallbackArguments,
|
|
mut rv: v8::ReturnValue<v8::Value>,
|
|
) {
|
|
fn empty(
|
|
_scope: &mut v8::HandleScope,
|
|
_args: v8::FunctionCallbackArguments,
|
|
_rv: v8::ReturnValue<v8::Value>,
|
|
) {
|
|
}
|
|
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 wrap = Wrap {
|
|
value: v8::TracedReference::new(scope, args.get(0)),
|
|
};
|
|
let member = unsafe {
|
|
v8::cppgc::make_garbage_collected(scope.get_cpp_heap().unwrap(), wrap)
|
|
};
|
|
|
|
unsafe {
|
|
v8::Object::wrap::<TAG, Wrap>(scope, obj, &member);
|
|
}
|
|
|
|
rv.set(obj.into());
|
|
}
|
|
|
|
fn op_unwrap(
|
|
scope: &mut v8::HandleScope,
|
|
args: v8::FunctionCallbackArguments,
|
|
mut rv: v8::ReturnValue,
|
|
) {
|
|
let obj = args.get(0).try_into().unwrap();
|
|
let member = unsafe { v8::Object::unwrap::<TAG, Wrap>(scope, obj) };
|
|
rv.set(member.unwrap().value.get(scope).unwrap());
|
|
}
|
|
|
|
{
|
|
// Create a managed heap.
|
|
let mut heap = v8::cppgc::Heap::create(
|
|
guard.platform.clone(),
|
|
v8::cppgc::HeapCreateParams::default(),
|
|
);
|
|
let isolate = &mut v8::Isolate::new(v8::CreateParams::default());
|
|
isolate.attach_cpp_heap(&mut heap);
|
|
|
|
{
|
|
let handle_scope = &mut v8::HandleScope::new(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, op_wrap).unwrap();
|
|
let name = v8::String::new(scope, "wrap").unwrap();
|
|
global.set(scope, name.into(), func.into()).unwrap();
|
|
}
|
|
{
|
|
let func = v8::Function::new(scope, op_unwrap).unwrap();
|
|
let name = v8::String::new(scope, "unwrap").unwrap();
|
|
global.set(scope, name.into(), func.into()).unwrap();
|
|
}
|
|
|
|
execute_script(
|
|
scope,
|
|
r#"
|
|
{
|
|
const x = {};
|
|
const y = unwrap(wrap(x)); // collected
|
|
if (x !== y) {
|
|
throw new Error('mismatch');
|
|
}
|
|
}
|
|
|
|
globalThis.wrapped = wrap(wrap({})); // not collected
|
|
"#,
|
|
);
|
|
|
|
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 0);
|
|
|
|
{
|
|
let mut vec = Vec::<u8>::new();
|
|
scope.take_heap_snapshot(|chunk| {
|
|
vec.extend_from_slice(chunk);
|
|
true
|
|
});
|
|
let s = std::str::from_utf8(&vec).unwrap();
|
|
assert!(s.contains("Eyecatcher"));
|
|
}
|
|
|
|
scope.request_garbage_collection_for_testing(
|
|
v8::GarbageCollectionType::Full,
|
|
);
|
|
|
|
assert!(TRACE_COUNT.load(Ordering::SeqCst) > 0);
|
|
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 1);
|
|
|
|
execute_script(
|
|
scope,
|
|
r#"
|
|
globalThis.wrapped = undefined;
|
|
"#,
|
|
);
|
|
|
|
scope.request_garbage_collection_for_testing(
|
|
v8::GarbageCollectionType::Full,
|
|
);
|
|
|
|
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3);
|
|
}
|
|
|
|
isolate.detach_cpp_heap();
|
|
heap.terminate();
|
|
drop(heap);
|
|
}
|
|
}
|
|
|
|
fn execute_script(
|
|
context_scope: &mut v8::ContextScope<v8::HandleScope>,
|
|
source: &str,
|
|
) {
|
|
let scope = &mut v8::HandleScope::new(context_scope);
|
|
let scope = &mut v8::TryCatch::new(scope);
|
|
|
|
let source = v8::String::new(scope, source).unwrap();
|
|
|
|
let script =
|
|
v8::Script::compile(scope, source, None).expect("failed to compile script");
|
|
|
|
if script.run(scope).is_none() {
|
|
let exception_string = scope
|
|
.stack_trace()
|
|
.or_else(|| scope.exception())
|
|
.map(|value| value.to_rust_string_lossy(scope))
|
|
.unwrap_or_else(|| "no stack trace".into());
|
|
|
|
panic!("{}", exception_string);
|
|
}
|
|
}
|