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<'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.
///
/// 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,
        }
    }
}