blob: e380c8a21ebc32eaec6e8d976e29342addcf89c9 [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! It would be highly desirable to share a lot of this code with `value_param.rs`
10//! but this proves to be surprisingly fiddly.
11
12use cxx::{memory::UniquePtrTarget, UniquePtr};
13use moveit::MoveRef;
14use std::{
15 marker::{PhantomData, PhantomPinned},
16 pin::Pin,
17};
18
19/// A trait representing a parameter to a C++ function which is received
20/// by rvalue (i.e. by move).
21///
22/// # Panics
23///
24/// The implementations of this trait which take a [`cxx::UniquePtr`] will
25/// panic if the pointer is NULL.
26///
27/// # Safety
28///
29/// Implementers must guarantee that the pointer returned by `get_ptr`
30/// is of the correct size and alignment of `T`.
31pub unsafe trait RValueParam<T>: Sized {
32 /// Retrieve the pointer to the underlying item, to be passed to C++.
33 /// Note that on the C++ side this is currently passed to `std::move`
34 /// and therefore may be mutated.
35 #[doc(hidden)]
36 fn get_ptr(stack: Pin<&mut Self>) -> *mut T;
37}
38
39unsafe impl<T> RValueParam<T> for UniquePtr<T>
40where
41 T: UniquePtrTarget,
42{
43 fn get_ptr(stack: Pin<&mut Self>) -> *mut T {
44 // Safety: we won't move/swap the contents of the outer pin, nor of the
45 // type stored within the UniquePtr.
46 unsafe {
47 (Pin::into_inner_unchecked(
48 (*Pin::into_inner_unchecked(stack))
49 .as_mut()
50 .expect("Passed a NULL UniquePtr as a C++ rvalue parameter"),
51 )) as *mut T
52 }
53 }
54}
55
56unsafe impl<T> RValueParam<T> for Pin<Box<T>> {
57 fn get_ptr(stack: Pin<&mut Self>) -> *mut T {
58 // Safety: we won't move/swap the contents of the outer pin, nor of the
59 // type stored within the UniquePtr.
60 unsafe {
61 (Pin::into_inner_unchecked((*Pin::into_inner_unchecked(stack)).as_mut())) as *mut T
62 }
63 }
64}
65
66unsafe impl<'a, T> RValueParam<T> for Pin<MoveRef<'a, T>> {
67 fn get_ptr(stack: Pin<&mut Self>) -> *mut T {
68 // Safety: we won't move/swap the contents of the outer pin, nor of the
69 // type stored within the UniquePtr.
70 unsafe {
71 (Pin::into_inner_unchecked((*Pin::into_inner_unchecked(stack)).as_mut())) as *mut T
72 }
73 }
74}
75
76/// Implementation detail for how we pass rvalue parameters into C++.
77/// This type is instantiated by auto-generated autocxx code each time we
78/// need to pass a value parameter into C++, and will take responsibility
79/// for extracting that value parameter from the [`RValueParam`] and doing
80/// any later cleanup.
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070081///
82/// Because C++ move constructors may modify the original object, we consume
83/// the object and store it, pinned, until the call completes. This avoids any
84/// risk that C++ will mutate objects elsewhere in Rust-land, which could cause
85/// problems in the case of re-entrancy as references might exist to those
86/// other objects and Rust assumes there are no unexpected mutations of objects
87/// where references exist.
Brian Silverman4e662aa2022-05-11 23:10:19 -070088#[doc(hidden)]
89pub struct RValueParamHandler<T, RVP>
90where
91 RVP: RValueParam<T>,
92{
93 // We can't populate this on 'new' because the object may move.
94 // Hence this is an Option - it's None until populate is called.
95 space: Option<RVP>,
96 _pinned: PhantomPinned,
97 _data: PhantomData<T>,
98}
99
100impl<T, RVP: RValueParam<T>> RValueParamHandler<T, RVP> {
101 /// Populate this stack space if needs be. Note safety guarantees
102 /// on [`get_ptr`].
103 ///
104 /// # Safety
105 ///
106 /// Callers must guarantee that this type will not move
107 /// in memory between calls to [`populate`] and [`get_ptr`].
108 /// Callers must call [`populate`] exactly once prior to calling [`get_ptr`].
109 pub unsafe fn populate(self: Pin<&mut Self>, param: RVP) {
110 // Structural pinning, as documented in [`std::pin`].
111 // Safety: we will not move the contents of the pin.
112 *Pin::into_inner_unchecked(self.map_unchecked_mut(|s| &mut s.space)) = Some(param)
113 }
114
115 /// Return a pointer to the underlying value which can be passed to C++.
116 /// Per the unsafety contract of [`populate`], the object must not have moved
117 /// since it was created, and [`populate`] has been called exactly once
118 /// prior to this call.
119 pub fn get_ptr(self: Pin<&mut Self>) -> *mut T {
120 // Structural pinning, as documented in [`std::pin`]. `map_unchecked_mut` doesn't play
121 // nicely with `unwrap`, so we have to do it manually.
122 unsafe {
123 RVP::get_ptr(Pin::new_unchecked(
124 self.get_unchecked_mut().space.as_mut().unwrap(),
125 ))
126 }
127 }
128}
129
130impl<T, VP: RValueParam<T>> Default for RValueParamHandler<T, VP> {
131 fn default() -> Self {
132 Self {
133 space: None,
134 _pinned: PhantomPinned,
135 _data: PhantomData,
136 }
137 }
138}