autocxx/rvalue_param.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
// 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<T> RValueParam<T> for Pin<MoveRef<'_, 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.
///
/// Because C++ move constructors may modify the original object, we consume
/// the object and store it, pinned, until the call completes. This avoids any
/// risk that C++ will mutate objects elsewhere in Rust-land, which could cause
/// problems in the case of re-entrancy as references might exist to those
/// other objects and Rust assumes there are no unexpected mutations of objects
/// where references exist.
#[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,
}
}
}