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, CallContext, FromGodot, GodotConvert, GodotFfiVariant, 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 + fmt::Debug),* {
111            fn with_variants<F, R>(self, f: F) -> R
112            where
113                F: FnOnce(&[Variant]) -> R,
114            {
115                let ffi_args = (
116                    $(
117                        GodotType::into_ffi(ToGodot::to_godot(&self.$n)),
118                    )*
119                );
120
121                let variant_args = [
122                    $(
123                        GodotFfiVariant::ffi_to_variant(&ffi_args.$n),
124                    )*
125                ];
126
127                f(&variant_args)
128            }
129
130            fn with_variant_pointers<F, R>(self, f: F) -> R
131            where
132                F: FnOnce(&[godot_ffi::GDExtensionConstVariantPtr]) -> R,
133            {
134                self.with_variants(|variants| {
135                    let sys_args = [
136                        $(
137                            Variant::var_sys(&variants[$n]),
138                        )*
139                    ];
140                    f(&sys_args)
141                })
142            }
143
144            fn with_type_pointers<F, R>(self, f: F) -> R
145            where
146                F: FnOnce(&[godot_ffi::GDExtensionConstTypePtr]) -> R,
147            {
148                let ffi_args = (
149                    $(
150                        GodotType::into_ffi(ToGodot::to_godot(&self.$n)),
151                    )*
152                );
153
154                let ptr_args = [
155                    $(
156                        sys::GodotFfi::as_arg_ptr(&ffi_args.$n),
157                    )*
158                ];
159
160                f(&ptr_args)
161            }
162
163            fn to_variant_array(&self) -> Vec<Variant> {
164                let ($($p,)*) = self;
165
166                vec![
167                    $( $p.to_variant(), )*
168                ]
169            }
170        }
171    };
172}
173
174#[allow(unused_variables, unused_mut, clippy::unused_unit)]
175mod unit_impl {
176    use super::*;
177    unsafe_impl_param_tuple!();
178}
179unsafe_impl_param_tuple!((p0, 0): P0);
180unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1);
181unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2);
182unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3);
183unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4);
184unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5);
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);
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);
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);
188unsafe_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);
189unsafe_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);
190unsafe_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);
191unsafe_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);
192unsafe_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);
193
194/// Convert the `N`th argument of `args_ptr` into a value of type `P`.
195///
196/// # Safety
197/// - It must be safe to dereference the address at `args_ptr.offset(N)`.
198/// - The pointer at `args_ptr.offset(N)` must follow the safety requirements as laid out in
199///   [`GodotFfi::from_arg_ptr`].
200pub(super) unsafe fn ptrcall_arg<P: FromGodot, const N: isize>(
201    args_ptr: *const sys::GDExtensionConstTypePtr,
202    call_ctx: &CallContext,
203    call_type: sys::PtrcallType,
204) -> P {
205    // SAFETY: It is safe to dereference `args_ptr` at `N`.
206    let offset_ptr = unsafe { *args_ptr.offset(N) };
207
208    // SAFETY: The pointer follows the safety requirements from `GodotFfi::from_arg_ptr`.
209    let ffi = unsafe {
210        <P::Via as GodotType>::Ffi::from_arg_ptr(sys::force_mut_ptr(offset_ptr), call_type)
211    };
212
213    <P::Via as GodotType>::try_from_ffi(ffi)
214        .and_then(P::try_from_godot)
215        .unwrap_or_else(|err| param_error::<P>(call_ctx, N as i32, err))
216}
217
218/// Converts `arg` into a value of type `P`.
219///
220/// # Safety
221///
222/// - It must be safe to reborrow `arg` as a `&Variant` with a lifetime that lasts for the duration of the call.
223pub(super) unsafe fn varcall_arg<P: FromGodot>(
224    arg: sys::GDExtensionConstVariantPtr,
225    call_ctx: &CallContext,
226    param_index: isize,
227) -> Result<P, CallError> {
228    // SAFETY: It is safe to dereference `args_ptr` at `N` as a `Variant`.
229    let variant_ref = unsafe { Variant::borrow_var_sys(arg) };
230
231    variant_ref
232        .try_to_relaxed::<P>()
233        .map_err(|err| CallError::failed_param_conversion::<P>(call_ctx, param_index, err))
234}
235
236fn param_error<P>(call_ctx: &CallContext, index: i32, err: ConvertError) -> ! {
237    let param_ty = std::any::type_name::<P>();
238    panic!("in function `{call_ctx}` at parameter [{index}] of type {param_ty}: {err}");
239}
240
241fn assert_array_length<P: ParamTuple>(array: &[&Variant]) {
242    assert_eq!(
243        array.len(),
244        P::LEN,
245        "array {array:?} has wrong length, expected {} got {}",
246        P::LEN,
247        array.len()
248    );
249}
250
251#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
252mod test {
253    use super::*;
254
255    #[test]
256    fn format_args_test() {
257        assert_eq!(&().format_args(), "");
258        assert_eq!(&(1, 2, 3).format_args(), "1, 2, 3");
259    }
260
261    #[test]
262    fn count_idents_test() {
263        assert_eq!(2, count_idents!(a b));
264        assert_eq!(0, count_idents!());
265        assert_eq!(5, count_idents!(a b b a d));
266    }
267}