blob: 4acc52e091b2cb1fa4a678b991685201c116f351 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! It would be highly desirable to share a lot of this code with `value_param.rs`
//! but this proves to be surprisingly fiddly.
use cxx::{memory::UniquePtrTarget, UniquePtr};
use moveit::MoveRef;
use std::{
marker::{PhantomData, PhantomPinned},
pin::Pin,
};
/// A trait representing a parameter to a C++ function which is received
/// by rvalue (i.e. by move).
///
/// # Panics
///
/// The implementations of this trait which take a [`cxx::UniquePtr`] will
/// panic if the pointer is NULL.
///
/// # Safety
///
/// Implementers must guarantee that the pointer returned by `get_ptr`
/// is of the correct size and alignment of `T`.
pub unsafe trait RValueParam<T>: Sized {
/// Retrieve the pointer to the underlying item, to be passed to C++.
/// Note that on the C++ side this is currently passed to `std::move`
/// and therefore may be mutated.
#[doc(hidden)]
fn get_ptr(stack: Pin<&mut Self>) -> *mut T;
}
unsafe impl<T> RValueParam<T> for UniquePtr<T>
where
T: UniquePtrTarget,
{
fn get_ptr(stack: Pin<&mut Self>) -> *mut T {
// Safety: we won't move/swap the contents of the outer pin, nor of the
// type stored within the UniquePtr.
unsafe {
(Pin::into_inner_unchecked(
(*Pin::into_inner_unchecked(stack))
.as_mut()
.expect("Passed a NULL UniquePtr as a C++ rvalue parameter"),
)) as *mut T
}
}
}
unsafe impl<T> RValueParam<T> for Pin<Box<T>> {
fn get_ptr(stack: Pin<&mut Self>) -> *mut T {
// Safety: we won't move/swap the contents of the outer pin, nor of the
// type stored within the UniquePtr.
unsafe {
(Pin::into_inner_unchecked((*Pin::into_inner_unchecked(stack)).as_mut())) as *mut T
}
}
}
unsafe impl<'a, T> RValueParam<T> for Pin<MoveRef<'a, T>> {
fn get_ptr(stack: Pin<&mut Self>) -> *mut T {
// Safety: we won't move/swap the contents of the outer pin, nor of the
// type stored within the UniquePtr.
unsafe {
(Pin::into_inner_unchecked((*Pin::into_inner_unchecked(stack)).as_mut())) as *mut T
}
}
}
/// Implementation detail for how we pass rvalue parameters into C++.
/// This type is instantiated by auto-generated autocxx code each time we
/// need to pass a value parameter into C++, and will take responsibility
/// for extracting that value parameter from the [`RValueParam`] and doing
/// any later cleanup.
#[doc(hidden)]
pub struct RValueParamHandler<T, RVP>
where
RVP: RValueParam<T>,
{
// We can't populate this on 'new' because the object may move.
// Hence this is an Option - it's None until populate is called.
space: Option<RVP>,
_pinned: PhantomPinned,
_data: PhantomData<T>,
}
impl<T, RVP: RValueParam<T>> RValueParamHandler<T, RVP> {
/// Populate this stack space if needs be. Note safety guarantees
/// on [`get_ptr`].
///
/// # Safety
///
/// Callers must guarantee that this type will not move
/// in memory between calls to [`populate`] and [`get_ptr`].
/// Callers must call [`populate`] exactly once prior to calling [`get_ptr`].
pub unsafe fn populate(self: Pin<&mut Self>, param: RVP) {
// Structural pinning, as documented in [`std::pin`].
// Safety: we will not move the contents of the pin.
*Pin::into_inner_unchecked(self.map_unchecked_mut(|s| &mut s.space)) = Some(param)
}
/// Return a pointer to the underlying value which can be passed to C++.
/// Per the unsafety contract of [`populate`], the object must not have moved
/// since it was created, and [`populate`] has been called exactly once
/// prior to this call.
pub fn get_ptr(self: Pin<&mut Self>) -> *mut T {
// Structural pinning, as documented in [`std::pin`]. `map_unchecked_mut` doesn't play
// nicely with `unwrap`, so we have to do it manually.
unsafe {
RVP::get_ptr(Pin::new_unchecked(
self.get_unchecked_mut().space.as_mut().unwrap(),
))
}
}
}
impl<T, VP: RValueParam<T>> Default for RValueParamHandler<T, VP> {
fn default() -> Self {
Self {
space: None,
_pinned: PhantomPinned,
_data: PhantomData,
}
}
}