godot_core/meta/param_tuple/
impls.rs

1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8#![deny(unsafe_op_in_unsafe_fn)]
9#![allow(unused_attributes)]
10
11use std::fmt;
12
13use godot_ffi as sys;
14use sys::GodotFfi;
15
16use crate::builtin::Variant;
17use crate::meta::error::{CallError, ConvertError};
18use crate::meta::{
19    signature, ArgPassing, CallContext, FromGodot, GodotConvert, GodotType, InParamTuple,
20    OutParamTuple, ParamTuple, ToGodot,
21};
22
23macro_rules! count_idents {
24    () => { 0 };
25    ($id:ident $($rest:ident)*) => { 1 + count_idents!($($rest)*)};
26}
27
28macro_rules! unsafe_impl_param_tuple {
29    ($(($p:ident, $n:tt): $P:ident),*) => {
30        impl<$($P),*> ParamTuple for ($($P,)*) where $($P: GodotConvert + fmt::Debug),* {
31            const LEN: usize = count_idents!($($P)*);
32
33            #[doc(hidden)]
34            fn param_info(
35                index: usize,
36                param_name: &str,
37            ) -> Option<crate::registry::method::MethodParamOrReturnInfo> {
38                match index {
39                    $(
40                        $n => Some($P::Via::argument_info(param_name)),
41                    )*
42                    _ => None,
43                }
44            }
45
46            fn format_args(&self) -> String {
47                format!(
48                    // This repeat expression is basically just `"{$n:?}"`, the rest is only needed so that
49                    // the repetition separator can be `", "` instead of `,`.
50                    concat!("" $(, "{", $n, ":?}",)", "*),
51                    $(self.$n),*
52                )
53            }
54        }
55
56        impl<$($P),*> InParamTuple for ($($P,)*) where $($P: FromGodot + fmt::Debug),* {
57            unsafe fn from_varcall_args(
58                args_ptr: *const sys::GDExtensionConstVariantPtr,
59                call_ctx: &crate::meta::CallContext,
60            ) -> signature::CallResult<Self> {
61                let args = (
62                    $(
63                        // SAFETY: `args_ptr` is an array with length `Self::LEN` and each element is a valid pointer, since they
64                        // are all reborrowable as references.
65                        unsafe { *args_ptr.offset($n) },
66                    )*
67                );
68
69                let param_tuple = (
70                    $(
71                        // SAFETY: Each pointer in `args_ptr` is reborrowable as a `&Variant` for the duration of this call.
72                        unsafe { varcall_arg::<$P>(args.$n, call_ctx, $n)? },
73                    )*
74                );
75
76                Ok(param_tuple)
77            }
78
79            unsafe fn from_ptrcall_args(
80                args_ptr: *const sys::GDExtensionConstTypePtr,
81                call_type: sys::PtrcallType,
82                call_ctx: &crate::meta::CallContext,
83            ) -> Self {
84                (
85                    $(
86                        // SAFETY: `args_ptr` has length `Self::LEN` and `$n` is less than `Self::LEN`, and `args_ptr` must be an array whose
87                        // `$n`-th element is of type `$P`.
88                        unsafe { ptrcall_arg::<$P, $n>(args_ptr, call_ctx, call_type) },
89                    )*
90                )
91            }
92
93            fn from_variant_array(array: &[&Variant]) -> Self {
94                assert_array_length::<Self>(array);
95                let mut iter = array.iter();
96                (
97                    $(
98                        {
99                            let variant = iter.next().unwrap_or_else(
100                                || panic!("ParamTuple: {} access out-of-bounds (len {})", stringify!($p), array.len()));
101
102                            variant.to_relaxed_or_panic(
103                                || format!("ParamTuple: failed to convert parameter {}", stringify!($p)))
104                        },
105                    )*
106                )
107            }
108        }
109
110        impl<$($P),*> OutParamTuple for ($($P,)*) where $($P: ToGodot<Via: Clone> + fmt::Debug,)* {
111            fn with_variants<F, R>(self, f: F) -> R
112            where
113                F: FnOnce(&[Variant]) -> R,
114            {
115                let variant_args = [
116                    $(
117                        <$P::Pass as ArgPassing>::ref_to_variant(&self.$n),
118                    )*
119                ];
120
121                f(&variant_args)
122            }
123
124            fn with_variant_pointers<F, R>(self, f: F) -> R
125            where
126                F: FnOnce(&[godot_ffi::GDExtensionConstVariantPtr]) -> R,
127            {
128                self.with_variants(|variants| {
129                    let sys_args = [
130                        $(
131                            Variant::var_sys(&variants[$n]),
132                        )*
133                    ];
134                    f(&sys_args)
135                })
136            }
137
138            fn with_type_pointers<F, R>(self, f: F) -> R
139            where
140                F: FnOnce(&[godot_ffi::GDExtensionConstTypePtr]) -> R,
141            {
142                // Must be separate declarations, as pointers become invalid otherwise (UAF).
143                let ffi_args = (
144                    $(
145                        <$P::Pass as ArgPassing>::ref_to_ffi(&self.$n),
146                    )*
147                );
148
149                let ptr_args = [
150                    $(
151                        sys::GodotFfi::as_arg_ptr(&ffi_args.$n),
152                    )*
153                ];
154
155                f(&ptr_args)
156            }
157
158            fn to_variant_array(&self) -> Vec<Variant> {
159                let ($($p,)*) = self;
160
161                vec![
162                    $( $p.to_variant(), )*
163                ]
164            }
165        }
166    };
167}
168
169#[allow(unused_variables, unused_mut, clippy::unused_unit)]
170mod unit_impl {
171    use super::*;
172    unsafe_impl_param_tuple!();
173}
174unsafe_impl_param_tuple!((p0, 0): P0);
175unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1);
176unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2);
177unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3);
178unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4);
179unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5);
180unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6);
181unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7);
182unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8);
183unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9);
184unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10);
185unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11);
186unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11, (p12, 12): P12);
187unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11, (p12, 12): P12, (p13, 13): P13);
188
189/// Convert the `N`th argument of `args_ptr` into a value of type `P`.
190///
191/// # Safety
192/// - It must be safe to dereference the address at `args_ptr.offset(N)`.
193/// - The pointer at `args_ptr.offset(N)` must follow the safety requirements as laid out in
194///   [`GodotFfi::from_arg_ptr`].
195pub(super) unsafe fn ptrcall_arg<P: FromGodot, const N: isize>(
196    args_ptr: *const sys::GDExtensionConstTypePtr,
197    call_ctx: &CallContext,
198    call_type: sys::PtrcallType,
199) -> P {
200    // SAFETY: It is safe to dereference `args_ptr` at `N`.
201    let offset_ptr = unsafe { *args_ptr.offset(N) };
202
203    // SAFETY: The pointer follows the safety requirements from `GodotFfi::from_arg_ptr`.
204    let ffi = unsafe {
205        <P::Via as GodotType>::Ffi::from_arg_ptr(sys::force_mut_ptr(offset_ptr), call_type)
206    };
207
208    <P::Via as GodotType>::try_from_ffi(ffi)
209        .and_then(P::try_from_godot)
210        .unwrap_or_else(|err| param_error::<P>(call_ctx, N as i32, err))
211}
212
213/// Converts `arg` into a value of type `P`.
214///
215/// # Safety
216///
217/// - It must be safe to reborrow `arg` as a `&Variant` with a lifetime that lasts for the duration of the call.
218pub(super) unsafe fn varcall_arg<P: FromGodot>(
219    arg: sys::GDExtensionConstVariantPtr,
220    call_ctx: &CallContext,
221    param_index: isize,
222) -> Result<P, CallError> {
223    // SAFETY: It is safe to dereference `args_ptr` at `N` as a `Variant`.
224    let variant_ref = unsafe { Variant::borrow_var_sys(arg) };
225
226    variant_ref
227        .try_to_relaxed::<P>()
228        .map_err(|err| CallError::failed_param_conversion::<P>(call_ctx, param_index, err))
229}
230
231fn param_error<P>(call_ctx: &CallContext, index: i32, err: ConvertError) -> ! {
232    let param_ty = std::any::type_name::<P>();
233    panic!("in function `{call_ctx}` at parameter [{index}] of type {param_ty}: {err}");
234}
235
236fn assert_array_length<P: ParamTuple>(array: &[&Variant]) {
237    assert_eq!(
238        array.len(),
239        P::LEN,
240        "array {array:?} has wrong length, expected {} got {}",
241        P::LEN,
242        array.len()
243    );
244}
245
246// ----------------------------------------------------------------------------------------------------------------------------------------------
247
248#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
249mod test {
250    use super::*;
251
252    #[test]
253    fn format_args_test() {
254        assert_eq!(&().format_args(), "");
255        assert_eq!(&(1, 2, 3).format_args(), "1, 2, 3");
256    }
257
258    #[test]
259    fn count_idents_test() {
260        assert_eq!(2, count_idents!(a b));
261        assert_eq!(0, count_idents!());
262        assert_eq!(5, count_idents!(a b b a d));
263    }
264}