0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2025-01-21 05:02:11 -05:00

fix: let cppgc align to 16 bytes (#1677)

This commit is contained in:
snek 2025-01-06 13:59:24 +01:00 committed by GitHub
parent 25900a3c33
commit 8020991257
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 218 additions and 157 deletions

View file

@ -27,7 +27,7 @@ fn main() {
v8::V8::initialize_platform(platform.clone());
v8::V8::initialize();
v8::cppgc::initalize_process(platform.clone());
v8::cppgc::initialize_process(platform.clone());
{
let heap =

View file

@ -46,7 +46,7 @@ fn main() {
let platform = v8::new_default_platform(0, false).make_shared();
v8::V8::initialize_platform(platform.clone());
v8::V8::initialize();
v8::cppgc::initalize_process(platform.clone());
v8::cppgc::initialize_process(platform.clone());
{
// Create a managed heap.

View file

@ -3976,9 +3976,19 @@ void cppgc__heap__collect_garbage_for_testing(
heap->CollectGarbageForTesting(stack_state);
}
RustObj* cppgc__make_garbage_collectable(v8::CppHeap* heap, size_t size) {
return cppgc::MakeGarbageCollected<RustObj>(heap->GetAllocationHandle(),
cppgc::AdditionalBytes(size));
class alignas(16) RustObjButAlign16 : public RustObj {};
RustObj* cppgc__make_garbage_collectable(v8::CppHeap* heap, size_t size,
size_t alignment) {
if (alignment <= 8) {
return cppgc::MakeGarbageCollected<RustObj>(heap->GetAllocationHandle(),
cppgc::AdditionalBytes(size));
}
if (alignment <= 16) {
return cppgc::MakeGarbageCollected<RustObjButAlign16>(
heap->GetAllocationHandle(), cppgc::AdditionalBytes(size));
}
return nullptr;
}
void cppgc__Visitor__Trace__Member(cppgc::Visitor* visitor,

View file

@ -27,6 +27,7 @@ extern "C" {
fn cppgc__make_garbage_collectable(
heap: *mut Heap,
size: usize,
alignment: usize,
) -> *mut RustObj;
fn cppgc__heap__enable_detached_garbage_collections_for_testing(
@ -145,7 +146,7 @@ unsafe fn get_object_from_rust_obj<T: GarbageCollected>(
/// creating a Heap.
///
/// Can be called multiple times when paired with `ShutdownProcess()`.
pub fn initalize_process(platform: SharedRef<Platform>) {
pub fn initialize_process(platform: SharedRef<Platform>) {
unsafe {
cppgc__initialize_process(&*platform as *const Platform as *mut _);
}
@ -155,7 +156,7 @@ pub fn initalize_process(platform: SharedRef<Platform>) {
///
/// Must be called after destroying the last used heap. Some process-global
/// metadata may not be returned and reused upon a subsequent
/// `initalize_process()` call.
/// `initialize_process()` call.
pub unsafe fn shutdown_process() {
cppgc__shutdown_process();
}
@ -343,6 +344,11 @@ pub unsafe fn make_garbage_collected<T: GarbageCollected + 'static>(
heap: &Heap,
obj: T,
) -> Ptr<T> {
const {
// max alignment in cppgc is 16
assert!(std::mem::align_of::<T>() <= 16);
}
let additional_bytes = (object_offset_for_rust_obj::<T>()
- std::mem::size_of::<RustObj>())
+ std::mem::size_of::<T>();
@ -351,9 +357,12 @@ pub unsafe fn make_garbage_collected<T: GarbageCollected + 'static>(
cppgc__make_garbage_collectable(
heap as *const Heap as *mut _,
additional_bytes,
std::mem::align_of::<T>(),
)
};
assert!(!pointer.is_null());
unsafe {
let inner = get_object_from_rust_obj::<T>(pointer);
inner.write(obj);

View file

@ -193,8 +193,8 @@ struct three_pointers_t {
#endif // SUPPORT_H_
class RustObj final : public cppgc::GarbageCollected<RustObj>,
public cppgc::NameProvider {
class RustObj : public cppgc::GarbageCollected<RustObj>,
public cppgc::NameProvider {
public:
~RustObj();
void Trace(cppgc::Visitor* visitor) const;

View file

@ -2,177 +2,219 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use v8::cppgc::{GarbageCollected, Visitor};
struct CppGCGuard {
pub platform: v8::SharedRef<v8::Platform>,
}
mod setup {
use std::sync::Once;
use std::sync::RwLock;
// use std::sync::RwLockReadGuard;
use std::sync::RwLockWriteGuard;
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());
static PROCESS_LOCK: RwLock<()> = RwLock::new(());
CppGCGuard { platform }
}
/*
/// Set up global state for a test that can run in parallel with other tests.
pub(super) fn parallel_test() -> SetupGuard<RwLockReadGuard<'static, ()>> {
initialize_once();
SetupGuard::new(PROCESS_LOCK.read().unwrap())
}
*/
impl Drop for CppGCGuard {
fn drop(&mut self) {
unsafe {
v8::cppgc::shutdown_process();
v8::V8::dispose();
/// Set up global state for a test that must be the only test running.
pub(super) fn sequential_test() -> SetupGuard<RwLockWriteGuard<'static, ()>> {
initialize_once();
SetupGuard::new(PROCESS_LOCK.write().unwrap())
}
fn initialize_once() {
static START: Once = Once::new();
START.call_once(|| {
assert!(v8::icu::set_common_data_74(align_data::include_aligned!(
align_data::Align16,
"../third_party/icu/common/icudtl.dat"
))
.is_ok());
v8::V8::set_flags_from_string(
"--no_freeze_flags_after_init --expose_gc --harmony-import-assertions --harmony-shadow-realm --allow_natives_syntax --turbo_fast_api_calls",
);
let platform = v8::new_unprotected_default_platform(0, false).make_shared();
v8::V8::initialize_platform(platform.clone());
v8::V8::initialize();
v8::cppgc::initialize_process(platform.clone());
});
}
#[must_use]
pub(super) struct SetupGuard<G> {
_inner: G,
}
impl<G> SetupGuard<G> {
fn new(inner: G) -> Self {
Self { _inner: inner }
}
v8::V8::dispose_platform();
}
}
const TAG: u16 = 1;
#[test]
fn cppgc_object_wrap() {
let guard = initalize_test();
macro_rules! test {
( $( $decln:ident : $declt:ty )?, $( $initn:ident : $inite:expr )? ) => {{
let _guard = setup::sequential_test();
static TRACE_COUNT: AtomicUsize = AtomicUsize::new(0);
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
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();
struct Wrap {
$( #[allow(unused)] $decln: $declt , )?
value: v8::TracedReference<v8::Value>,
}
execute_script(
scope,
r#"
{
const x = {};
const y = unwrap(wrap(x)); // collected
if (x !== y) {
throw new Error('mismatch');
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")
}
}
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"));
impl Drop for Wrap {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
scope.request_garbage_collection_for_testing(
v8::GarbageCollectionType::Full,
);
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());
assert!(TRACE_COUNT.load(Ordering::SeqCst) > 0);
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 1);
let wrap = Wrap {
$( $initn: $inite , )?
value: v8::TracedReference::new(scope, args.get(0)),
};
let member = unsafe {
v8::cppgc::make_garbage_collected(scope.get_cpp_heap().unwrap(), wrap)
};
execute_script(
scope,
r#"
globalThis.wrapped = undefined;
"#,
);
unsafe {
v8::Object::wrap::<TAG, Wrap>(scope, obj, &member);
}
scope.request_garbage_collection_for_testing(
v8::GarbageCollectionType::Full,
);
rv.set(obj.into());
}
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3);
}
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());
}
isolate.detach_cpp_heap();
heap.terminate();
drop(heap);
}
{
// Create a managed heap.
let mut heap = v8::cppgc::Heap::create(
v8::V8::get_current_platform(),
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);
}
}}
}
#[test]
fn cppgc_object_wrap8() {
test!(,);
}
#[test]
fn cppgc_object_wrap16() {
test!(big: u128, big: 0);
}
fn execute_script(