diff --git a/godot-codegen/src/generator/builtins.rs b/godot-codegen/src/generator/builtins.rs index 4219fe314..bb609d992 100644 --- a/godot-codegen/src/generator/builtins.rs +++ b/godot-codegen/src/generator/builtins.rs @@ -253,7 +253,8 @@ fn make_builtin_method_definition( let ptrcall_invocation = quote! { let method_bind = sys::builtin_method_table().#fptr_access; - ::out_builtin_ptrcall( + + Signature::::out_builtin_ptrcall( method_bind, #builtin_name_str, #method_name_str, @@ -265,7 +266,7 @@ fn make_builtin_method_definition( let varcall_invocation = quote! { let method_bind = sys::builtin_method_table().#fptr_access; - ::out_builtin_ptrcall_varargs( + Signature::::out_builtin_ptrcall_varargs( method_bind, #builtin_name_str, #method_name_str, diff --git a/godot-codegen/src/generator/classes.rs b/godot-codegen/src/generator/classes.rs index ad3acfa08..63c8137eb 100644 --- a/godot-codegen/src/generator/classes.rs +++ b/godot-codegen/src/generator/classes.rs @@ -515,7 +515,7 @@ fn make_class_method_definition( let ptrcall_invocation = quote! { let method_bind = sys::#get_method_table().#fptr_access; - ::out_class_ptrcall( + Signature::::out_class_ptrcall( method_bind, #rust_class_name, #rust_method_name, @@ -528,7 +528,7 @@ fn make_class_method_definition( let varcall_invocation = quote! { let method_bind = sys::#get_method_table().#fptr_access; - ::out_class_varcall( + Signature::::out_class_varcall( method_bind, #rust_class_name, #rust_method_name, diff --git a/godot-codegen/src/generator/functions_common.rs b/godot-codegen/src/generator/functions_common.rs index b59b6e9e4..cc4891271 100644 --- a/godot-codegen/src/generator/functions_common.rs +++ b/godot-codegen/src/generator/functions_common.rs @@ -177,9 +177,9 @@ pub fn make_function_definition( let call_sig_decl = { let return_ty = &sig.return_value().type_tokens(); - // Build <'a0, 'a1, ...> for lifetimes. quote! { - type CallSig #callsig_lifetime_args = ( #return_ty, #(#param_types),* ); + type CallRet = #return_ty; + type CallParams #callsig_lifetime_args = (#(#param_types,)*); } }; diff --git a/godot-codegen/src/generator/utility_functions.rs b/godot-codegen/src/generator/utility_functions.rs index 6bcf16ad3..c6587ff6f 100644 --- a/godot-codegen/src/generator/utility_functions.rs +++ b/godot-codegen/src/generator/utility_functions.rs @@ -46,7 +46,7 @@ pub(crate) fn make_utility_function_definition(function: &UtilityFunction) -> To let ptrcall_invocation = quote! { let utility_fn = sys::utility_function_table().#function_ident; - ::out_utility_ptrcall( + Signature::::out_utility_ptrcall( utility_fn, #function_name_str, args @@ -56,7 +56,7 @@ pub(crate) fn make_utility_function_definition(function: &UtilityFunction) -> To let varcall_invocation = quote! { let utility_fn = sys::utility_function_table().#function_ident; - ::out_utility_ptrcall_varargs( + Signature::::out_utility_ptrcall_varargs( utility_fn, #function_name_str, args, diff --git a/godot-codegen/src/util.rs b/godot-codegen/src/util.rs index 0efbaf0da..bb86222e6 100644 --- a/godot-codegen/src/util.rs +++ b/godot-codegen/src/util.rs @@ -25,7 +25,7 @@ pub fn make_imports() -> TokenStream { quote! { use godot_ffi as sys; use crate::builtin::*; - use crate::meta::{AsArg, AsObjectArg, ClassName, CowArg, ObjectArg, ObjectCow, PtrcallSignatureTuple, RefArg, VarcallSignatureTuple}; + use crate::meta::{AsArg, AsObjectArg, ClassName, CowArg, InParamTuple, ObjectArg, ObjectCow, OutParamTuple, ParamTuple, RefArg, Signature}; use crate::classes::native::*; use crate::classes::Object; use crate::obj::Gd; diff --git a/godot-core/src/meta/godot_convert/mod.rs b/godot-core/src/meta/godot_convert/mod.rs index 041ed2fdc..9f0aaa53a 100644 --- a/godot-core/src/meta/godot_convert/mod.rs +++ b/godot-core/src/meta/godot_convert/mod.rs @@ -120,19 +120,6 @@ pub trait FromGodot: Sized + GodotConvert { } } -pub(crate) fn into_ffi_variant(value: &T) -> Variant { - let via = value.to_godot(); - let ffi = via.to_ffi(); - GodotFfiVariant::ffi_to_variant(&ffi) -} - -pub(crate) fn try_from_ffi( - ffi: ::Ffi, -) -> Result { - let via = ::try_from_ffi(ffi)?; - T::try_from_godot(via) -} - #[macro_export] macro_rules! impl_godot_as_self { ($T:ty) => { diff --git a/godot-core/src/meta/mod.rs b/godot-core/src/meta/mod.rs index 7f5eaacff..17bb82953 100644 --- a/godot-core/src/meta/mod.rs +++ b/godot-core/src/meta/mod.rs @@ -48,6 +48,7 @@ mod array_type_info; mod class_name; mod godot_convert; mod method_info; +mod param_tuple; mod property_info; mod signature; mod traits; @@ -61,8 +62,7 @@ pub use class_name::ClassName; pub use godot_convert::{FromGodot, GodotConvert, ToGodot}; pub use traits::{ArrayElement, GodotType, PackedArrayElement}; -#[cfg(since_api = "4.2")] -pub use crate::registry::signal::variadic::ParamTuple; +pub use param_tuple::{InParamTuple, OutParamTuple, ParamTuple}; pub(crate) use array_type_info::ArrayTypeInfo; pub(crate) use traits::{ diff --git a/godot-core/src/meta/param_tuple.rs b/godot-core/src/meta/param_tuple.rs new file mode 100644 index 000000000..4404759dd --- /dev/null +++ b/godot-core/src/meta/param_tuple.rs @@ -0,0 +1,93 @@ +/* + * Copyright (c) godot-rust; Bromeon and contributors. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use crate::builtin::Variant; + +use super::{CallContext, CallResult, PropertyInfo}; +use godot_ffi as sys; + +mod impls; + +/// Represents a parameter list as Rust tuple where each tuple element is one parameter. +/// +/// This trait only contains metadata for the parameter list, the actual functionality is contained in [`InParamTuple`] and +/// [`OutParamTuple`]. +pub trait ParamTuple: Sized { + /// The number of elements in this parameter list. + const LEN: usize; + + /// The param info of the parameter at index `index`. + #[doc(hidden)] + fn param_info( + index: usize, + param_name: &str, + ) -> Option; + + /// The property info of the parameter at index `index`. + fn property_info(index: usize, param_name: &str) -> Option { + Self::param_info(index, param_name).map(|param| param.info) + } + + /// Return a string representing the arguments. + fn format_args(&self) -> String; +} + +/// Represents a parameter list that is received from some external location (usually Godot). +/// +/// As an example, this would be used for user-defined functions that will be called from Godot, however this is _not_ used when +/// calling a Godot function from Rust code. +pub trait InParamTuple: ParamTuple { + /// Converts `args_ptr` to `Self` by first going through [`Variant`]. + /// + /// # Safety + /// + /// - `args_ptr` must be a pointer to an array of length [`Self::LEN`](ParamTuple::LEN) + /// - Each element of `args_ptr` must be reborrowable as a `&Variant` with a lifetime that lasts for the duration of the call. + unsafe fn from_varcall_args( + args_ptr: *const sys::GDExtensionConstVariantPtr, + call_ctx: &CallContext, + ) -> CallResult; + + /// Converts `args_ptr` to `Self` directly. + /// + /// # Safety + /// + /// - `args_ptr` must be a pointer to a valid array of length [`Self::LEN`](ParamTuple::LEN) + /// - each element of `args_ptr` must be of the same type as each element of `Self` + unsafe fn from_ptrcall_args( + args_ptr: *const sys::GDExtensionConstTypePtr, + call_type: sys::PtrcallType, + call_ctx: &CallContext, + ) -> Self; + + /// Converts `array` to `Self` by calling [`from_variant`](crate::meta::FromGodot::from_variant) on each argument. + fn from_variant_array(array: &[&Variant]) -> Self; +} + +/// Represents a parameter list that is used to call some external code. +/// +/// As an example, this would be used to call Godot functions through FFI, however this is _not_ used when Godot calls a user-defined +/// function. +pub trait OutParamTuple: ParamTuple { + /// Call `f` on the tuple `self` by first converting `self` to an array of [`Variant`]s. + fn with_variants(self, f: F) -> R + where + F: FnOnce(&[Variant]) -> R; + + /// Call `f` on the tuple `self` by first converting `self` to an array of [`Variant`] pointers. + fn with_variant_pointers(self, f: F) -> R + where + F: FnOnce(&[sys::GDExtensionConstVariantPtr]) -> R; + + /// Call `f` on the tuple `self` by first converting `self` to an array of Godot type pointers. + fn with_type_pointers(self, f: F) -> R + where + F: FnOnce(&[sys::GDExtensionConstTypePtr]) -> R; + + /// Converts `array` to `Self` by calling [`to_variant`](crate::meta::ToGodot::to_variant) on each argument. + fn to_variant_array(&self) -> Vec; +} diff --git a/godot-core/src/meta/param_tuple/impls.rs b/godot-core/src/meta/param_tuple/impls.rs new file mode 100644 index 000000000..3746d1a5e --- /dev/null +++ b/godot-core/src/meta/param_tuple/impls.rs @@ -0,0 +1,260 @@ +/* + * Copyright (c) godot-rust; Bromeon and contributors. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#![deny(unsafe_op_in_unsafe_fn)] +#![allow(unused_attributes)] + +use crate::builtin::Variant; +use crate::meta::error::{CallError, ConvertError}; +use crate::meta::{ + signature, CallContext, FromGodot, GodotConvert, GodotFfiVariant, GodotType, InParamTuple, + OutParamTuple, ParamTuple, ToGodot, +}; +use godot_ffi as sys; +use std::fmt; +use sys::GodotFfi; + +macro_rules! count_idents { + () => { 0 }; + ($id:ident $($rest:ident)*) => { 1 + count_idents!($($rest)*)}; +} + +macro_rules! unsafe_impl_param_tuple { + ($(($p:ident, $n:tt): $P:ident),*) => { + impl<$($P),*> ParamTuple for ($($P,)*) where $($P: GodotConvert + fmt::Debug),* { + const LEN: usize = count_idents!($($P)*); + + #[doc(hidden)] + fn param_info( + index: usize, + param_name: &str, + ) -> Option { + match index { + $( + $n => Some($P::Via::argument_info(param_name)), + )* + _ => None, + } + } + + fn format_args(&self) -> String { + format!( + // This repeat expression is basically just `"{$n:?}"`, the rest is only needed so that + // the repetition separator can be `", "` instead of `,`. + concat!("" $(, "{", $n, ":?}",)", "*), + $(self.$n),* + ) + } + } + + impl<$($P),*> InParamTuple for ($($P,)*) where $($P: FromGodot + fmt::Debug),* { + unsafe fn from_varcall_args( + args_ptr: *const sys::GDExtensionConstVariantPtr, + call_ctx: &crate::meta::CallContext, + ) -> signature::CallResult { + let args = ( + $( + // SAFETY: `args_ptr` is an array with length `Self::LEN` and each element is a valid pointer, since they + // are all reborrowable as references. + unsafe { *args_ptr.offset($n) }, + )* + ); + + let param_tuple = ( + $( + // SAFETY: Each pointer in `args_ptr` is reborrowable as a `&Variant` for the duration of this call. + unsafe { varcall_arg::<$P>(args.$n, call_ctx, $n)? }, + )* + ); + + Ok(param_tuple) + } + + unsafe fn from_ptrcall_args( + args_ptr: *const sys::GDExtensionConstTypePtr, + call_type: sys::PtrcallType, + call_ctx: &crate::meta::CallContext, + ) -> Self { + ( + $( + // SAFETY: `args_ptr` has length `Self::LEN` and `$n` is less than `Self::LEN`, and `args_ptr` must be an array whose + // `$n`-th element is of type `$P`. + unsafe { ptrcall_arg::<$P, $n>(args_ptr, call_ctx, call_type) }, + )* + ) + } + + fn from_variant_array(array: &[&Variant]) -> Self { + assert_array_length::(array); + let mut iter = array.iter(); + ( + $( + <$P>::from_variant( + iter.next().unwrap_or_else(|| panic!("ParamTuple: {} access out-of-bounds (len {})", stringify!($p), array.len())) + ), + )* + ) + } + } + + impl<$($P),*> OutParamTuple for ($($P,)*) where $($P: ToGodot + fmt::Debug),* { + fn with_variants(self, f: F) -> R + where + F: FnOnce(&[Variant]) -> R, + { + let ffi_args = ( + $( + GodotType::into_ffi(ToGodot::to_godot(&self.$n)), + )* + ); + + let variant_args = [ + $( + GodotFfiVariant::ffi_to_variant(&ffi_args.$n), + )* + ]; + + f(&variant_args) + } + + fn with_variant_pointers(self, f: F) -> R + where + F: FnOnce(&[godot_ffi::GDExtensionConstVariantPtr]) -> R, + { + self.with_variants(|variants| { + let sys_args = [ + $( + Variant::var_sys(&variants[$n]), + )* + ]; + f(&sys_args) + }) + } + + fn with_type_pointers(self, f: F) -> R + where + F: FnOnce(&[godot_ffi::GDExtensionConstTypePtr]) -> R, + { + let ffi_args = ( + $( + GodotType::into_ffi(ToGodot::to_godot(&self.$n)), + )* + ); + + let ptr_args = [ + $( + sys::GodotFfi::as_arg_ptr(&ffi_args.$n), + )* + ]; + + f(&ptr_args) + } + + fn to_variant_array(&self) -> Vec { + let ($($p,)*) = self; + + vec![ + $( $p.to_variant(), )* + ] + } + } + }; +} + +#[allow(unused_variables, unused_mut, clippy::unused_unit)] +mod unit_impl { + use super::*; + unsafe_impl_param_tuple!(); +} +unsafe_impl_param_tuple!((p0, 0): P0); +unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1); +unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2); +unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3); +unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4); +unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5); +unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6); +unsafe_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); +unsafe_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); +unsafe_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); +unsafe_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); +unsafe_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); +unsafe_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); +unsafe_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); + +/// Convert the `N`th argument of `args_ptr` into a value of type `P`. +/// +/// # Safety +/// - It must be safe to dereference the address at `args_ptr.offset(N)`. +/// - The pointer at `args_ptr.offset(N)` must follow the safety requirements as laid out in +/// [`GodotFfi::from_arg_ptr`]. +pub(super) unsafe fn ptrcall_arg( + args_ptr: *const sys::GDExtensionConstTypePtr, + call_ctx: &CallContext, + call_type: sys::PtrcallType, +) -> P { + // SAFETY: It is safe to dereference `args_ptr` at `N`. + let offset_ptr = unsafe { *args_ptr.offset(N) }; + + // SAFETY: The pointer follows the safety requirements from `GodotFfi::from_arg_ptr`. + let ffi = unsafe { + ::Ffi::from_arg_ptr(sys::force_mut_ptr(offset_ptr), call_type) + }; + + ::try_from_ffi(ffi) + .and_then(P::try_from_godot) + .unwrap_or_else(|err| param_error::

(call_ctx, N as i32, err)) +} + +/// Converts `arg` into a value of type `P`. +/// +/// # Safety +/// +/// - It must be safe to reborrow `arg` as a `&Variant` with a lifetime that lasts for the duration of the call. +pub(super) unsafe fn varcall_arg( + arg: sys::GDExtensionConstVariantPtr, + call_ctx: &CallContext, + param_index: isize, +) -> Result { + // SAFETY: It is safe to dereference `args_ptr` at `N` as a `Variant`. + let variant_ref = unsafe { Variant::borrow_var_sys(arg) }; + + P::try_from_variant(variant_ref) + .map_err(|err| CallError::failed_param_conversion::

(call_ctx, param_index, err)) +} + +fn param_error

(call_ctx: &CallContext, index: i32, err: ConvertError) -> ! { + let param_ty = std::any::type_name::

(); + panic!("in function `{call_ctx}` at parameter [{index}] of type {param_ty}: {err}"); +} + +fn assert_array_length(array: &[&Variant]) { + assert_eq!( + array.len(), + P::LEN, + "array {array:?} has wrong length, expected {} got {}", + P::LEN, + array.len() + ); +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn format_args_test() { + assert_eq!(&().format_args(), ""); + assert_eq!(&(1, 2, 3).format_args(), "1, 2, 3"); + } + + #[test] + fn count_idents_test() { + assert_eq!(2, count_idents!(a b)); + assert_eq!(0, count_idents!()); + assert_eq!(5, count_idents!(a b b a d)); + } +} diff --git a/godot-core/src/meta/signature.rs b/godot-core/src/meta/signature.rs index 2df06abde..86a9889fd 100644 --- a/godot-core/src/meta/signature.rs +++ b/godot-core/src/meta/signature.rs @@ -7,225 +7,153 @@ use std::borrow::Cow; use std::fmt; -use std::fmt::Debug; - -use godot_ffi as sys; -use sys::{BuiltinMethodBind, ClassMethodBind, GodotFfi, UtilityFunctionBind}; +use std::marker::PhantomData; use crate::builtin::Variant; use crate::meta::error::{CallError, ConvertError}; -use crate::meta::godot_convert::{into_ffi_variant, try_from_ffi}; -use crate::meta::*; +use crate::meta::{ + FromGodot, GodotConvert, GodotType, InParamTuple, MethodParamOrReturnInfo, OutParamTuple, + ParamTuple, ToGodot, +}; use crate::obj::{GodotClass, InstanceId}; -// TODO: -// separate arguments and return values, so that a type can be used in function arguments even if it doesn't -// implement `ToGodot`, and the other way around for return values. +use godot_ffi::{self as sys, GodotFfi}; + +pub(super) type CallResult = Result; + +//mod impls; + +/// A full signature for a function. +/// +/// For in-calls (that is, calls from the Godot engine to Rust code) `Params` will implement [`InParamTuple`] and `Ret` +/// will implement [`ToGodot`]. +/// +/// For out-calls (that is calls from Rust code to the Godot engine) `Params` will implement [`OutParamTuple`] and `Ret` +/// will implement [`FromGodot`]. +pub struct Signature { + _p: PhantomData, + _r: PhantomData, +} -#[doc(hidden)] -pub trait VarcallSignatureTuple: PtrcallSignatureTuple { - const PARAM_COUNT: usize; +impl Signature { + pub fn param_names(param_names: &[&str]) -> Vec { + assert_eq!( + param_names.len(), + Params::LEN, + "`param_names` should contain one name for each parameter" + ); - fn param_property_info(index: usize, param_name: &str) -> PropertyInfo; - fn param_info(index: usize, param_name: &str) -> Option; - fn return_info() -> Option; + param_names + .iter() + .enumerate() + .map(|(index, param_name)| Params::param_info(index, param_name).unwrap()) + .collect() + } +} - // TODO(uninit) - can we use this for varcall/ptrcall? - // ret: sys::GDExtensionUninitializedVariantPtr - // ret: sys::GDExtensionUninitializedTypePtr - unsafe fn in_varcall( +/// In-calls: +/// +/// Calls going from the Godot engine to Rust code. +#[deny(unsafe_op_in_unsafe_fn)] +impl Signature { + /// Receive a varcall from Godot, and return the value in `ret` as a variant pointer. + /// + /// # Safety + /// + /// A call to this function must be caused by Godot making a varcall with parameters `Params` and return type `Ret`. + #[inline] + pub unsafe fn in_varcall( instance_ptr: sys::GDExtensionClassInstancePtr, call_ctx: &CallContext, args_ptr: *const sys::GDExtensionConstVariantPtr, arg_count: i64, ret: sys::GDExtensionVariantPtr, err: *mut sys::GDExtensionCallError, - func: fn(sys::GDExtensionClassInstancePtr, Self::Params) -> Self::Ret, - ) -> Result<(), CallError>; - - unsafe fn out_class_varcall( - method_bind: ClassMethodBind, - // Separate parameters to reduce tokens in generated class API. - class_name: &'static str, - method_name: &'static str, - object_ptr: sys::GDExtensionObjectPtr, - maybe_instance_id: Option, // if not static - args: Self::Params, - varargs: &[Variant], - ) -> Result; + func: unsafe fn(sys::GDExtensionClassInstancePtr, Params) -> Ret, + ) -> CallResult<()> { + //$crate::out!("in_varcall: {call_ctx}"); + CallError::check_arg_count(call_ctx, arg_count as usize, Params::LEN)?; - /// Outbound virtual call to a method overridden by a script attached to the object. - /// - /// Returns `None` if the script does not override the method. - #[cfg(since_api = "4.3")] - unsafe fn out_script_virtual_call( - // Separate parameters to reduce tokens in macro-generated API. - class_name: &'static str, - method_name: &'static str, - method_sname_ptr: sys::GDExtensionConstStringNamePtr, - object_ptr: sys::GDExtensionObjectPtr, - args: Self::Params, - ) -> Self::Ret; + #[cfg(feature = "trace")] + trace::push(true, false, call_ctx); - unsafe fn out_utility_ptrcall_varargs( - utility_fn: UtilityFunctionBind, - function_name: &'static str, - args: Self::Params, - varargs: &[Variant], - ) -> Self::Ret; + // SAFETY: TODO. + let args = unsafe { Params::from_varcall_args(args_ptr, call_ctx)? }; - unsafe fn out_builtin_ptrcall_varargs( - builtin_fn: BuiltinMethodBind, - class_name: &'static str, - method_name: &'static str, - type_ptr: sys::GDExtensionTypePtr, - args: Self::Params, - varargs: &[Variant], - ) -> Self::Ret; - - fn format_args(args: &Self::Params) -> String; -} - -#[doc(hidden)] -pub trait PtrcallSignatureTuple { - type Params; - type Ret; + let rust_result = unsafe { func(instance_ptr, args) }; + // SAFETY: TODO. + unsafe { varcall_return::(rust_result, ret, err) }; + Ok(()) + } - // Note: this method imposes extra bounds on GodotFfi, which may not be implemented for user types. - // We could fall back to varcalls in such cases, and not require GodotFfi categorically. - unsafe fn in_ptrcall( + /// Receive a ptrcall from Godot, and return the value in `ret` as a type pointer. + /// + /// # Safety + /// + /// A call to this function must be caused by Godot making a ptrcall with parameters `Params` and return type `Ret`. + #[inline] + pub unsafe fn in_ptrcall( instance_ptr: sys::GDExtensionClassInstancePtr, - call_ctx: &CallContext<'static>, + call_ctx: &CallContext, args_ptr: *const sys::GDExtensionConstTypePtr, ret: sys::GDExtensionTypePtr, - func: fn(sys::GDExtensionClassInstancePtr, Self::Params) -> Self::Ret, + func: fn(sys::GDExtensionClassInstancePtr, Params) -> Ret, call_type: sys::PtrcallType, - ); + ) { + // $crate::out!("in_ptrcall: {call_ctx}"); + + #[cfg(feature = "trace")] + trace::push(true, true, call_ctx); + + // SAFETY: TODO. + let args = unsafe { Params::from_ptrcall_args(args_ptr, call_type, call_ctx) }; + + // SAFETY: + // `ret` is always a pointer to an initialized value of type $R + // TODO: double-check the above + unsafe { ptrcall_return::(func(instance_ptr, args), ret, call_ctx, call_type) } + } +} - unsafe fn out_class_ptrcall( - method_bind: ClassMethodBind, +/// Out-calls: +/// +/// Calls going from the rust code to the Godot engine. +#[deny(unsafe_op_in_unsafe_fn)] +impl Signature { + /// Make a varcall to the Godot engine for a class method. + /// + /// # Safety + /// + /// - `object_ptr` must be a live instance of a class with the type expected by `method_bind` + /// - `method_bind` must expect explicit args `args`, varargs `varargs`, and return a value of type `Ret` + #[inline] + pub unsafe fn out_class_varcall( + method_bind: sys::ClassMethodBind, // Separate parameters to reduce tokens in generated class API. class_name: &'static str, method_name: &'static str, object_ptr: sys::GDExtensionObjectPtr, maybe_instance_id: Option, // if not static - args: Self::Params, - ) -> Self::Ret; - - unsafe fn out_builtin_ptrcall( - builtin_fn: BuiltinMethodBind, - // Separate parameters to reduce tokens in generated class API. - class_name: &'static str, - method_name: &'static str, - type_ptr: sys::GDExtensionTypePtr, - args: Self::Params, - ) -> Self::Ret; - - unsafe fn out_utility_ptrcall( - utility_fn: UtilityFunctionBind, - function_name: &'static str, - args: Self::Params, - ) -> Self::Ret; -} - -macro_rules! impl_varcall_signature_for_tuple { - ( - $PARAM_COUNT:literal; - $R:ident - $(, ($pn:ident, $n:tt) : $Pn:ident)* // $n cannot be literal if substituted as tuple index .0 - ) => { - #[allow(unused_variables)] - impl<$R, $($Pn,)*> VarcallSignatureTuple for ($R, $($Pn,)*) - where - $R: ToGodot + FromGodot + Debug, - $( - $Pn: ToGodot + FromGodot + Debug, - )* - { - const PARAM_COUNT: usize = $PARAM_COUNT; - - #[inline] - fn param_info(index: usize, param_name: &str) -> Option { - match index { - $( - $n => Some($Pn::Via::argument_info(param_name)), - )* - _ => None, - } - } + args: Params, + varargs: &[Variant], + ) -> CallResult { + let call_ctx = CallContext::outbound(class_name, method_name); + //$crate::out!("out_class_varcall: {call_ctx}"); - #[inline] - fn return_info() -> Option { - $R::Via::return_info() - } + // Note: varcalls are not safe from failing, if they happen through an object pointer -> validity check necessary. + if let Some(instance_id) = maybe_instance_id { + crate::classes::ensure_object_alive(instance_id, object_ptr, &call_ctx); + } - #[inline] - fn param_property_info(index: usize, param_name: &str) -> PropertyInfo { - match index { - $( - $n => $Pn::Via::property_info(param_name), - )* - _ => unreachable!("property_info: unavailable for index {}", index), - } - } + let class_fn = sys::interface_fn!(object_method_bind_call); - #[inline] - unsafe fn in_varcall( - instance_ptr: sys::GDExtensionClassInstancePtr, - call_ctx: &CallContext, - args_ptr: *const sys::GDExtensionConstVariantPtr, - arg_count: i64, - ret: sys::GDExtensionVariantPtr, - err: *mut sys::GDExtensionCallError, - func: fn(sys::GDExtensionClassInstancePtr, Self::Params) -> Self::Ret, - ) -> Result<(), CallError> { - //$crate::out!("in_varcall: {call_ctx}"); - CallError::check_arg_count(call_ctx, arg_count as usize, $PARAM_COUNT)?; - - #[cfg(feature = "trace")] - trace::push(true, false, &call_ctx); - - let args = ($( - unsafe { varcall_arg::<$Pn, $n>(args_ptr, call_ctx)? }, - )*) ; - - let rust_result = func(instance_ptr, args); - varcall_return::<$R>(rust_result, ret, err); - Ok(()) - } + let variant = args.with_variants(|explicit_args| { + let mut variant_ptrs = Vec::with_capacity(explicit_args.len() + varargs.len()); + variant_ptrs.extend(explicit_args.iter().map(Variant::var_sys)); + variant_ptrs.extend(varargs.iter().map(Variant::var_sys)); - #[inline] - unsafe fn out_class_varcall( - method_bind: ClassMethodBind, - // Separate parameters to reduce tokens in generated class API. - class_name: &'static str, - method_name: &'static str, - object_ptr: sys::GDExtensionObjectPtr, - maybe_instance_id: Option, // if not static - ($($pn,)*): Self::Params, - varargs: &[Variant], - ) -> Result { - let call_ctx = CallContext::outbound(class_name, method_name); - //$crate::out!("out_class_varcall: {call_ctx}"); - - // Note: varcalls are not safe from failing, if they happen through an object pointer -> validity check necessary. - if let Some(instance_id) = maybe_instance_id { - crate::classes::ensure_object_alive(instance_id, object_ptr, &call_ctx); - } - - let class_fn = sys::interface_fn!(object_method_bind_call); - - let explicit_args = [ - $( - into_ffi_variant(&$pn), - )* - ]; - - let mut variant_ptrs = Vec::with_capacity(explicit_args.len() + varargs.len()); - variant_ptrs.extend(explicit_args.iter().map(Variant::var_sys)); - variant_ptrs.extend(varargs.iter().map(Variant::var_sys)); - - let variant: Result = Variant::new_with_var_uninit_result(|return_ptr| { + unsafe { + Variant::new_with_var_uninit_result(|return_ptr| { let mut err = sys::default_call_error(); class_fn( method_bind.0, @@ -233,292 +161,236 @@ macro_rules! impl_varcall_signature_for_tuple { variant_ptrs.as_ptr(), variant_ptrs.len() as i64, return_ptr, - std::ptr::addr_of_mut!(err), + &raw mut err, ); - CallError::check_out_varcall(&call_ctx, err, &explicit_args, varargs) - }); - - variant.and_then(|v| { - v.try_to::() - .map_err(|e| CallError::failed_return_conversion::(&call_ctx, e)) + CallError::check_out_varcall(&call_ctx, err, explicit_args, varargs) }) } + }); + + variant.and_then(|v| { + v.try_to::() + .map_err(|e| CallError::failed_return_conversion::(&call_ctx, e)) + }) + } + + /// Make a varcall to the Godot engine for a virtual function call. + /// + /// # Safety + /// + /// - `object_ptr` must be a live instance of a class with a method named `method_sname_ptr` + /// - The method must expect args `args`, and return a value of type `Ret` + #[cfg(since_api = "4.3")] + #[inline] + pub unsafe fn out_script_virtual_call( + // Separate parameters to reduce tokens in macro-generated API. + class_name: &'static str, + method_name: &'static str, + method_sname_ptr: sys::GDExtensionConstStringNamePtr, + object_ptr: sys::GDExtensionObjectPtr, + args: Params, + ) -> Ret { + // Assumes that caller has previously checked existence of a virtual method. + + let call_ctx = CallContext::outbound(class_name, method_name); + //$crate::out!("out_script_virtual_call: {call_ctx}"); - #[cfg(since_api = "4.3")] - unsafe fn out_script_virtual_call( - // Separate parameters to reduce tokens in macro-generated API. - class_name: &'static str, - method_name: &'static str, - method_sname_ptr: sys::GDExtensionConstStringNamePtr, - object_ptr: sys::GDExtensionObjectPtr, - ($($pn,)*): Self::Params, - ) -> Self::Ret { - // Assumes that caller has previously checked existence of a virtual method. - - let call_ctx = CallContext::outbound(class_name, method_name); - //$crate::out!("out_script_virtual_call: {call_ctx}"); - - let object_call_script_method = sys::interface_fn!(object_call_script_method); - let explicit_args = [ - $( - into_ffi_variant(&$pn), - )* - ]; - - let variant_ptrs = explicit_args.iter().map(Variant::var_sys).collect::>(); - - let variant = Variant::new_with_var_uninit(|return_ptr| { + let object_call_script_method = sys::interface_fn!(object_call_script_method); + + let variant = args.with_variant_pointers(|sys_args| { + // SAFETY: TODO. + unsafe { + Variant::new_with_var_uninit(|return_ptr| { let mut err = sys::default_call_error(); object_call_script_method( object_ptr, method_sname_ptr, - variant_ptrs.as_ptr(), - variant_ptrs.len() as i64, + sys_args.as_ptr(), + sys_args.len() as i64, return_ptr, - std::ptr::addr_of_mut!(err), + &raw mut err, ); - }); - - let result = ::try_from_variant(&variant); - result.unwrap_or_else(|err| return_error::(&call_ctx, err)) + }) } + }); - // Note: this is doing a ptrcall, but uses variant conversions for it. - #[inline] - unsafe fn out_utility_ptrcall_varargs( - utility_fn: UtilityFunctionBind, - function_name: &'static str, - ($($pn,)*): Self::Params, - varargs: &[Variant], - ) -> Self::Ret { - let call_ctx = CallContext::outbound("", function_name); - //$crate::out!("out_utility_ptrcall_varargs: {call_ctx}"); - - let explicit_args: [Variant; $PARAM_COUNT] = [ - $( - into_ffi_variant(&$pn), - )* - ]; + let result = ::try_from_variant(&variant); + result.unwrap_or_else(|err| return_error::(&call_ctx, err)) + } + /// Make a ptrcall to the Godot engine for a utility function that has varargs. + /// + /// # Safety + /// + /// - `utility_fn` must expect args `args`, varargs `varargs`, and return a value of type `Ret` + // Note: this is doing a ptrcall, but uses variant conversions for it. + #[inline] + pub unsafe fn out_utility_ptrcall_varargs( + utility_fn: sys::UtilityFunctionBind, + function_name: &'static str, + args: Params, + varargs: &[Variant], + ) -> Ret { + let call_ctx = CallContext::outbound("", function_name); + //$crate::out!("out_utility_ptrcall_varargs: {call_ctx}"); + + unsafe { + Self::raw_ptrcall(args, &call_ctx, |explicit_args, return_ptr| { let mut type_ptrs = Vec::with_capacity(explicit_args.len() + varargs.len()); - type_ptrs.extend(explicit_args.iter().map(sys::GodotFfi::sys)); + type_ptrs.extend(explicit_args.iter()); type_ptrs.extend(varargs.iter().map(sys::GodotFfi::sys)); // Important: this calls from_sys_init_default(). - let result = new_from_ptrcall::(|return_ptr| { - utility_fn(return_ptr, type_ptrs.as_ptr(), type_ptrs.len() as i32); - }); - result.unwrap_or_else(|err| return_error::(&call_ctx, err)) - } + // SAFETY: TODO. + utility_fn(return_ptr, type_ptrs.as_ptr(), type_ptrs.len() as i32); + }) + } + } - #[inline] - unsafe fn out_builtin_ptrcall_varargs( - builtin_fn: BuiltinMethodBind, - class_name: &'static str, - method_name: &'static str, - type_ptr: sys::GDExtensionTypePtr, - ($($pn,)*): Self::Params, - varargs: &[Variant], - ) -> Self::Ret { - let call_ctx = CallContext::outbound(class_name, method_name); - //$crate::out!("out_builtin_ptrcall_varargs: {call_ctx}"); - - let explicit_args: [Variant; $PARAM_COUNT] = [ - $( - into_ffi_variant(&$pn), - )* - ]; + /// Make a ptrcall to the Godot engine for a builtin method that has varargs. + /// + /// # Safety + /// + /// - `builtin_fn` must expect args `args`, varargs `varargs`, and return a value of type `Ret` + #[inline] + pub unsafe fn out_builtin_ptrcall_varargs( + builtin_fn: sys::BuiltinMethodBind, + class_name: &'static str, + method_name: &'static str, + type_ptr: sys::GDExtensionTypePtr, + args: Params, + varargs: &[Variant], + ) -> Ret { + let call_ctx = CallContext::outbound(class_name, method_name); + //$crate::out!("out_builtin_ptrcall_varargs: {call_ctx}"); + unsafe { + Self::raw_ptrcall(args, &call_ctx, |explicit_args, return_ptr| { let mut type_ptrs = Vec::with_capacity(explicit_args.len() + varargs.len()); - type_ptrs.extend(explicit_args.iter().map(sys::GodotFfi::sys)); + type_ptrs.extend(explicit_args.iter()); type_ptrs.extend(varargs.iter().map(sys::GodotFfi::sys)); // Important: this calls from_sys_init_default(). - let result = new_from_ptrcall::(|return_ptr| { - builtin_fn(type_ptr, type_ptrs.as_ptr(), return_ptr, type_ptrs.len() as i32); - }); - result.unwrap_or_else(|err| return_error::(&call_ctx, err)) - } - - #[inline] - fn format_args(args: &Self::Params) -> String { - let mut string = String::new(); - $( - string.push_str(&format!("{:?}, ", args.$n)); - )* - string.remove(string.len() - 2); // remove trailing ", " - string - } + builtin_fn( + type_ptr, + type_ptrs.as_ptr(), + return_ptr, + type_ptrs.len() as i32, + ); + }) } - }; -} - -macro_rules! marshal_args { - ( - let $out:ident = $( $pn:ident: $n:tt )* ; - ) => { - // Note: this used to be done in a single `into_ffi()` function, however with reference semantics, this causes issues with lifetimes: - // The to_godot() call creates a temporary value local to the function, and even when using the same 'v lifetime throughout, rustc - // assumes that the temporary value may store other state that would go out of scope after the function returns. - // Thus, this is now done in 2 steps, and abstracted with a macro. - - #[allow(clippy::let_unit_value)] - let vias = ( - $( - ToGodot::to_godot(&$pn), - )* - ); - - #[allow(clippy::let_unit_value)] - let $out = ( - $( - GodotType::to_ffi(&vias.$n), - )* - ); } -} -macro_rules! impl_ptrcall_signature_for_tuple { - ( - $R:ident - $(, ($pn:ident, $n:tt) : $Pn:ident)* // $n cannot be literal if substituted as tuple index .0 - ) => { - #[allow(unused_variables)] - impl<$R, $($Pn,)*> PtrcallSignatureTuple for ($R, $($Pn,)*) - where $R: ToGodot + FromGodot + Debug, - $( $Pn: ToGodot + FromGodot + Debug, )* - { - type Params = ($($Pn,)*); - type Ret = $R; - - #[inline] - unsafe fn in_ptrcall( - instance_ptr: sys::GDExtensionClassInstancePtr, - call_ctx: &CallContext, - args_ptr: *const sys::GDExtensionConstTypePtr, - ret: sys::GDExtensionTypePtr, - func: fn(sys::GDExtensionClassInstancePtr, Self::Params) -> Self::Ret, - call_type: sys::PtrcallType, - ) { - // $crate::out!("in_ptrcall: {call_ctx}"); - - #[cfg(feature = "trace")] - trace::push(true, true, &call_ctx); - - let args = ($( - unsafe { ptrcall_arg::<$Pn, $n>(args_ptr, call_ctx, call_type) }, - )*) ; - - // SAFETY: - // `ret` is always a pointer to an initialized value of type $R - // TODO: double-check the above - ptrcall_return::<$R>(func(instance_ptr, args), ret, call_ctx, call_type) - } + /// Make a ptrcall to the Godot engine for a class method. + /// + /// # Safety + /// + /// - `object_ptr` must be a live instance of a class with the type expected by `method_bind` + /// - `method_bind` must expect explicit args `args`, and return a value of type `Ret` + #[inline] + pub unsafe fn out_class_ptrcall( + method_bind: sys::ClassMethodBind, + // Separate parameters to reduce tokens in generated class API. + class_name: &'static str, + method_name: &'static str, + object_ptr: sys::GDExtensionObjectPtr, + maybe_instance_id: Option, // if not static + args: Params, + ) -> Ret { + let call_ctx = CallContext::outbound(class_name, method_name); + // $crate::out!("out_class_ptrcall: {call_ctx}"); - #[inline] - unsafe fn out_class_ptrcall( - method_bind: ClassMethodBind, - // Separate parameters to reduce tokens in generated class API. - class_name: &'static str, - method_name: &'static str, - object_ptr: sys::GDExtensionObjectPtr, - maybe_instance_id: Option, // if not static - ($($pn,)*): Self::Params, - ) -> Self::Ret { - let call_ctx = CallContext::outbound(class_name, method_name); - // $crate::out!("out_class_ptrcall: {call_ctx}"); - - if let Some(instance_id) = maybe_instance_id { - crate::classes::ensure_object_alive(instance_id, object_ptr, &call_ctx); - } - - let class_fn = sys::interface_fn!(object_method_bind_ptrcall); - - marshal_args! { - let marshalled_args = $($pn: $n)*; - } - - let type_ptrs = [ - $( - sys::GodotFfi::as_arg_ptr(&marshalled_args.$n), - )* - ]; - - let result = new_from_ptrcall::(|return_ptr| { - class_fn(method_bind.0, object_ptr, type_ptrs.as_ptr(), return_ptr); - }); - result.unwrap_or_else(|err| return_error::(&call_ctx, err)) - } + if let Some(instance_id) = maybe_instance_id { + crate::classes::ensure_object_alive(instance_id, object_ptr, &call_ctx); + } - #[inline] - unsafe fn out_builtin_ptrcall( - builtin_fn: BuiltinMethodBind, - // Separate parameters to reduce tokens in generated class API. - class_name: &'static str, - method_name: &'static str, - type_ptr: sys::GDExtensionTypePtr, - ($($pn,)*): Self::Params, - ) -> Self::Ret { - let call_ctx = CallContext::outbound(class_name, method_name); - // $crate::out!("out_builtin_ptrcall: {call_ctx}"); - - marshal_args! { - let marshalled_args = $($pn: $n)*; - } - - let type_ptrs = [ - $( - sys::GodotFfi::as_arg_ptr(&marshalled_args.$n), - )* - ]; - - let result = new_from_ptrcall::(|return_ptr| { - builtin_fn(type_ptr, type_ptrs.as_ptr(), return_ptr, type_ptrs.len() as i32); - }); - result.unwrap_or_else(|err| return_error::(&call_ctx, err)) - } + let class_fn = sys::interface_fn!(object_method_bind_ptrcall); + + unsafe { + Self::raw_ptrcall(args, &call_ctx, |explicit_args, return_ptr| { + class_fn( + method_bind.0, + object_ptr, + explicit_args.as_ptr(), + return_ptr, + ); + }) + } + } - #[inline] - unsafe fn out_utility_ptrcall( - utility_fn: UtilityFunctionBind, - function_name: &'static str, - ($($pn,)*): Self::Params, - ) -> Self::Ret { - let call_ctx = CallContext::outbound("", function_name); - // $crate::out!("out_utility_ptrcall: {call_ctx}"); - - marshal_args! { - let marshalled_args = $($pn: $n)*; - } - - let arg_ptrs = [ - $( - sys::GodotFfi::as_arg_ptr(&marshalled_args.$n), - )* - ]; - - let result = new_from_ptrcall::(|return_ptr| { - utility_fn(return_ptr, arg_ptrs.as_ptr(), arg_ptrs.len() as i32); - }); - result.unwrap_or_else(|err| return_error::(&call_ctx, err)) - } + /// Make a ptrcall to the Godot engine for a builtin method. + /// + /// # Safety + /// + /// - `builtin_fn` must expect explicit args `args`, and return a value of type `Ret` + #[inline] + pub unsafe fn out_builtin_ptrcall( + builtin_fn: sys::BuiltinMethodBind, + // Separate parameters to reduce tokens in generated class API. + class_name: &'static str, + method_name: &'static str, + type_ptr: sys::GDExtensionTypePtr, + args: Params, + ) -> Ret { + let call_ctx = CallContext::outbound(class_name, method_name); + // $crate::out!("out_builtin_ptrcall: {call_ctx}"); + + unsafe { + Self::raw_ptrcall(args, &call_ctx, |explicit_args, return_ptr| { + builtin_fn( + type_ptr, + explicit_args.as_ptr(), + return_ptr, + explicit_args.len() as i32, + ); + }) } - }; -} + } -/// Convert the `N`th argument of `args_ptr` into a value of type `P`. -/// -/// # Safety -/// - It must be safe to dereference the pointer at `args_ptr.offset(N)` . -unsafe fn varcall_arg( - args_ptr: *const sys::GDExtensionConstVariantPtr, - call_ctx: &CallContext, -) -> Result { - let variant_ref = Variant::borrow_var_sys(*args_ptr.offset(N)); - - P::try_from_variant(variant_ref) - .map_err(|err| CallError::failed_param_conversion::

(call_ctx, N, err)) + /// Make a ptrcall to the Godot engine for a utility function. + /// + /// # Safety + /// + /// - `utility_fn` must expect explicit args `args`, and return a value of type `Ret` + #[inline] + pub unsafe fn out_utility_ptrcall( + utility_fn: sys::UtilityFunctionBind, + function_name: &'static str, + args: Params, + ) -> Ret { + let call_ctx = CallContext::outbound("", function_name); + // $crate::out!("out_utility_ptrcall: {call_ctx}"); + + unsafe { + Self::raw_ptrcall(args, &call_ctx, |explicit_args, return_ptr| { + utility_fn( + return_ptr, + explicit_args.as_ptr(), + explicit_args.len() as i32, + ); + }) + } + } + + /// Performs a ptrcall and processes the return value to give nice error output. + /// + /// # Safety + /// + /// This calls [`GodotFfi::new_with_init`] and passes the ptr as the second argument to `f`, see that function for safety docs. + unsafe fn raw_ptrcall( + args: Params, + call_ctx: &CallContext, + f: impl FnOnce(&[sys::GDExtensionConstTypePtr], sys::GDExtensionTypePtr), + ) -> Ret { + let ffi = args.with_type_pointers(|explicit_args| unsafe { + <::Ffi>::new_with_init(|return_ptr| f(explicit_args, return_ptr)) + }); + + Ret::Via::try_from_ffi(ffi) + .and_then(Ret::try_from_godot) + .unwrap_or_else(|err| return_error::(call_ctx, err)) + } } /// Moves `ret_val` into `ret`. @@ -555,25 +427,6 @@ pub(crate) unsafe fn varcall_return_checked( } } -/// Convert the `N`th argument of `args_ptr` into a value of type `P`. -/// -/// # Safety -/// - It must be safe to dereference the address at `args_ptr.offset(N)` . -/// - The pointer at `args_ptr.offset(N)` must follow the safety requirements as laid out in -/// [`GodotFuncMarshal::try_from_arg`][sys::GodotFuncMarshal::try_from_arg]. -unsafe fn ptrcall_arg( - args_ptr: *const sys::GDExtensionConstTypePtr, - call_ctx: &CallContext, - call_type: sys::PtrcallType, -) -> P { - let ffi = ::Ffi::from_arg_ptr( - sys::force_mut_ptr(*args_ptr.offset(N)), - call_type, - ); - - try_from_ffi(ffi).unwrap_or_else(|err| param_error::

(call_ctx, N as i32, err)) -} - /// Moves `ret_val` into `ret`. /// /// # Safety @@ -591,65 +444,11 @@ unsafe fn ptrcall_return( ffi.move_return_ptr(ret, call_type); } -fn param_error

(call_ctx: &CallContext, index: i32, err: ConvertError) -> ! { - let param_ty = std::any::type_name::

(); - panic!("in function `{call_ctx}` at parameter [{index}] of type {param_ty}: {err}"); -} - fn return_error(call_ctx: &CallContext, err: ConvertError) -> ! { let return_ty = std::any::type_name::(); panic!("in function `{call_ctx}` at return type {return_ty}: {err}"); } -unsafe fn new_from_ptrcall( - process_return_ptr: impl FnOnce(sys::GDExtensionTypePtr), -) -> Result { - let ffi = <::Ffi as sys::GodotFfi>::new_with_init(|return_ptr| { - process_return_ptr(return_ptr) - }); - - T::Via::try_from_ffi(ffi).and_then(T::try_from_godot) -} - -// ---------------------------------------------------------------------------------------------------------------------------------------------- -// Poor man's variadic templates. -// For example, RenderingServer::environment_set_volumetric_fog() has 14 parameters. We may need to extend this if the API adds more such methods. - -impl_varcall_signature_for_tuple!(0; R); -impl_varcall_signature_for_tuple!(1; R, (p0, 0): P0); -impl_varcall_signature_for_tuple!(2; R, (p0, 0): P0, (p1, 1): P1); -impl_varcall_signature_for_tuple!(3; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2); -impl_varcall_signature_for_tuple!(4; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3); -impl_varcall_signature_for_tuple!(5; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4); -impl_varcall_signature_for_tuple!(6; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5); -impl_varcall_signature_for_tuple!(7; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6); -impl_varcall_signature_for_tuple!(8; R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7); -impl_varcall_signature_for_tuple!(9; R, (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); -impl_varcall_signature_for_tuple!(10; R, (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); -impl_varcall_signature_for_tuple!(11; R, (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); -impl_varcall_signature_for_tuple!(12; R, (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); -impl_varcall_signature_for_tuple!(13; R, (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); -impl_varcall_signature_for_tuple!(14; R, (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); - -impl_ptrcall_signature_for_tuple!(R); -impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0); -impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1); -impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2); -impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3); -impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4); -impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5); -impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6); -impl_ptrcall_signature_for_tuple!(R, (p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7); -impl_ptrcall_signature_for_tuple!(R, (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); -impl_ptrcall_signature_for_tuple!(R, (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); -impl_ptrcall_signature_for_tuple!(R, (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); -impl_ptrcall_signature_for_tuple!(R, (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); -impl_ptrcall_signature_for_tuple!(R, (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); -impl_ptrcall_signature_for_tuple!(R, (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); - -// ---------------------------------------------------------------------------------------------------------------------------------------------- -// Information about function and method calls. - // Lazy Display, so we don't create tens of thousands of extra string literals. #[derive(Clone)] #[doc(hidden)] // currently exposed in godot::meta @@ -698,8 +497,6 @@ impl fmt::Display for CallContext<'_> { } } -// ---------------------------------------------------------------------------------------------------------------------------------------------- -// Trace diagnostics for integration tests #[cfg(feature = "trace")] pub mod trace { use std::cell::Cell; diff --git a/godot-core/src/registry/method.rs b/godot-core/src/registry/method.rs index ec325fb58..c2393bc7d 100644 --- a/godot-core/src/registry/method.rs +++ b/godot-core/src/registry/method.rs @@ -10,12 +10,12 @@ use sys::interface_fn; use crate::builtin::{StringName, Variant}; use crate::global::MethodFlags; -use crate::meta::{ClassName, PropertyInfo, VarcallSignatureTuple}; +use crate::meta::{ClassName, GodotConvert, GodotType, ParamTuple, PropertyInfo, Signature}; use crate::obj::GodotClass; /// Info relating to an argument or return type in a method. pub struct MethodParamOrReturnInfo { - info: PropertyInfo, + pub(crate) info: PropertyInfo, metadata: sys::GDExtensionClassMethodArgumentMetadata, } @@ -53,7 +53,7 @@ impl ClassMethodInfo { /// `call_func` and `ptrcall_func`, if provided, must: /// /// - Follow the behavior expected from the `method_flags`. - pub unsafe fn from_signature( + pub unsafe fn from_signature( method_name: StringName, call_func: sys::GDExtensionClassMethodCall, ptrcall_func: sys::GDExtensionClassMethodPtrCall, @@ -61,23 +61,8 @@ impl ClassMethodInfo { param_names: &[&str], // default_arguments: Vec, - not yet implemented ) -> Self { - let return_value = S::return_info(); - let mut arguments = Vec::new(); - - assert_eq!( - param_names.len(), - S::PARAM_COUNT, - "`param_names` should contain one name for each parameter" - ); - - for (i, name) in param_names.iter().enumerate().take(S::PARAM_COUNT) { - arguments.push(S::param_info(i, name).unwrap_or_else(|| { - panic!( - "signature with `PARAM_COUNT = {}` should have argument info for index `{i}`", - S::PARAM_COUNT - ) - })) - } + let return_value = Ret::Via::return_info(); + let arguments = Signature::::param_names(param_names); let default_arguments = vec![]; // not yet implemented. assert!( diff --git a/godot-core/src/registry/signal/connect_builder.rs b/godot-core/src/registry/signal/connect_builder.rs index 11fcaff6e..60eb07431 100644 --- a/godot-core/src/registry/signal/connect_builder.rs +++ b/godot-core/src/registry/signal/connect_builder.rs @@ -87,6 +87,7 @@ impl<'ts, 'c, CSig: WithSignals, Ps: meta::ParamTuple> ConnectBuilder<'ts, 'c, C > where F: SignalReceiver<(), Ps>, + Ps: meta::InParamTuple + 'static, { let godot_fn = make_godot_fn(move |args| { function.call((), args); @@ -145,6 +146,7 @@ impl<'ts, 'c, CSig: WithSignals, CRcv: GodotClass, Ps: meta::ParamTuple> where CRcv: GodotClass + Bounds, for<'c_rcv> F: SignalReceiver<&'c_rcv mut CRcv, Ps>, + Ps: meta::InParamTuple, { let mut gd: Gd = self.receiver_obj; let godot_fn = make_godot_fn(move |args| { @@ -177,6 +179,7 @@ impl<'ts, 'c, CSig: WithSignals, CRcv: GodotClass, Ps: meta::ParamTuple> where CRcv: GodotClass + Bounds, for<'c_rcv> F: SignalReceiver<&'c_rcv CRcv, Ps>, + Ps: meta::InParamTuple, { let gd: Gd = self.receiver_obj; let godot_fn = make_godot_fn(move |args| { @@ -333,7 +336,7 @@ impl BuilderData { pub(super) fn make_godot_fn(mut input: F) -> impl FnMut(&[&Variant]) -> Result where F: FnMut(Ps), - Ps: meta::ParamTuple, + Ps: meta::InParamTuple, { move |variant_args: &[&Variant]| -> Result { let args = Ps::from_variant_array(variant_args); diff --git a/godot-core/src/registry/signal/typed_signal.rs b/godot-core/src/registry/signal/typed_signal.rs index 7185e5990..43c70fb98 100644 --- a/godot-core/src/registry/signal/typed_signal.rs +++ b/godot-core/src/registry/signal/typed_signal.rs @@ -106,7 +106,10 @@ impl<'c, C: WithSignals, Ps: meta::ParamTuple> TypedSignal<'c, C, Ps> { /// /// This is intended for generic use. Typically, you'll want to use the more specific `emit()` method of the code-generated signal /// type, which also has named parameters. - pub fn emit_tuple(&mut self, args: Ps) { + pub fn emit_tuple(&mut self, args: Ps) + where + Ps: meta::OutParamTuple, + { let name = self.name.as_ref(); self.object.with_object_mut(|obj| { @@ -128,6 +131,7 @@ impl<'c, C: WithSignals, Ps: meta::ParamTuple> TypedSignal<'c, C, Ps> { pub fn connect(&mut self, mut function: F) where F: SignalReceiver<(), Ps>, + Ps: meta::InParamTuple + 'static, { let godot_fn = make_godot_fn(move |args| { function.call((), args); @@ -144,6 +148,7 @@ impl<'c, C: WithSignals, Ps: meta::ParamTuple> TypedSignal<'c, C, Ps> { where OtherC: GodotClass + Bounds, for<'c_rcv> F: SignalReceiver<&'c_rcv mut OtherC, Ps>, + Ps: meta::InParamTuple + 'static, { let mut gd = object.to_signal_obj(); // let mut gd = gd.to_owned_object(); @@ -208,7 +213,7 @@ impl<'c, C: WithSignals, Ps: meta::ParamTuple> TypedSignal<'c, C, Ps> { } } -impl TypedSignal<'_, C, Ps> { +impl TypedSignal<'_, C, Ps> { /// Connect a method (member function) with `&mut self` as the first parameter. /// /// To connect to methods on other objects, use [`connect_obj()`][Self::connect_obj]. \ diff --git a/godot-core/src/registry/signal/variadic.rs b/godot-core/src/registry/signal/variadic.rs index 94912011c..e493c645a 100644 --- a/godot-core/src/registry/signal/variadic.rs +++ b/godot-core/src/registry/signal/variadic.rs @@ -11,9 +11,6 @@ // // Could be generalized with R return type, and not special-casing `self`. But keep simple until actually needed. -use crate::builtin::Variant; -use crate::meta; - /// Trait that is implemented for functions that can be connected to signals. /// // Direct RustDoc link doesn't work, for whatever reason again... @@ -28,46 +25,11 @@ pub trait SignalReceiver: 'static { fn call(&mut self, maybe_instance: I, params: Ps); } -/// Represents a parameter list as Rust tuple. -/// -/// Each tuple element is one parameter. This trait provides conversions to and from `Variant` arrays. -// Re-exported under crate::meta. Might be worth splitting, but depends a bit on SignatureVarcall/Ptrcall refactoring. -pub trait ParamTuple: 'static { - fn to_variant_array(&self) -> Vec; - fn from_variant_array(array: &[&Variant]) -> Self; -} - // ---------------------------------------------------------------------------------------------------------------------------------------------- // Generated impls macro_rules! impl_signal_recipient { ($( $args:ident : $Ps:ident ),*) => { - // -------------------------------------------------------------------------------------------------------------------------------------- - // ParamTuple - - impl<$($Ps),*> ParamTuple for ($($Ps,)*) - where - $($Ps: meta::ToGodot + meta::FromGodot + 'static),* - { - fn to_variant_array(&self) -> Vec { - let ($($args,)*) = self; - - vec![ - $( $args.to_variant(), )* - ] - } - - #[allow(unused_variables, unused_mut, clippy::unused_unit)] - fn from_variant_array(array: &[&Variant]) -> Self { - let mut iter = array.iter(); - ( $( - <$Ps>::from_variant( - iter.next().unwrap_or_else(|| panic!("ParamTuple: {} access out-of-bounds (len {})", stringify!($args), array.len())) - ), - )* ) - } - } - // -------------------------------------------------------------------------------------------------------------------------------------- // SignalReceiver diff --git a/godot-core/src/task/futures.rs b/godot-core/src/task/futures.rs index 90f17f18e..fc888fade 100644 --- a/godot-core/src/task/futures.rs +++ b/godot-core/src/task/futures.rs @@ -16,7 +16,7 @@ use std::thread::ThreadId; use crate::builtin::{Callable, RustCallable, Signal, Variant}; use crate::classes::object::ConnectFlags; use crate::meta::sealed::Sealed; -use crate::meta::ParamTuple; +use crate::meta::InParamTuple; use crate::obj::{EngineBitfield, Gd, GodotClass, WithSignals}; use crate::registry::signal::TypedSignal; @@ -32,15 +32,15 @@ pub(crate) use crate::impl_dynamic_send; /// - If one of the signal arguments is `!Send`, but the signal was emitted on a different thread. /// - The future's `Drop` implementation can cause a non-unwinding panic in rare cases, should the signal object be freed at the same time /// as the future is dropped. Make sure to keep signal objects alive until there are no pending futures anymore. -pub struct SignalFuture(FallibleSignalFuture); +pub struct SignalFuture(FallibleSignalFuture); -impl SignalFuture { +impl SignalFuture { fn new(signal: Signal) -> Self { Self(FallibleSignalFuture::new(signal)) } } -impl Future for SignalFuture { +impl Future for SignalFuture { type Output = R; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -110,7 +110,7 @@ impl PartialEq for SignalFutureResolver { } } -impl RustCallable for SignalFutureResolver { +impl RustCallable for SignalFutureResolver { fn invoke(&mut self, args: &[&Variant]) -> Result { let waker = { let mut data = self.data.lock().unwrap(); @@ -185,13 +185,13 @@ impl SignalFutureState { /// - If one of the signal arguments is `!Send`, but the signal was emitted on a different thread. /// - The future's `Drop` implementation can cause a non-unwinding panic in rare cases, should the signal object be freed at the same time /// as the future is dropped. Make sure to keep signal objects alive until there are no pending futures anymore. -pub struct FallibleSignalFuture { +pub struct FallibleSignalFuture { data: Arc>>, callable: SignalFutureResolver, signal: Signal, } -impl FallibleSignalFuture { +impl FallibleSignalFuture { fn new(signal: Signal) -> Self { debug_assert!( !signal.is_null(), @@ -214,6 +214,7 @@ impl FallibleSignalFuture { signal, } } + fn poll(&mut self, cx: &mut Context<'_>) -> Poll> { let mut data = self.data.lock().unwrap(); @@ -256,7 +257,7 @@ impl Display for FallibleSignalFutureError { impl std::error::Error for FallibleSignalFutureError {} -impl Future for FallibleSignalFuture { +impl Future for FallibleSignalFuture { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -264,7 +265,7 @@ impl Future for FallibleSignalFuture { } } -impl Drop for FallibleSignalFuture { +impl Drop for FallibleSignalFuture { fn drop(&mut self) { // The callable might alredy be destroyed, this occurs during engine shutdown. if self.signal.is_null() { @@ -301,7 +302,7 @@ impl Signal { /// /// Since the `Signal` type does not contain information on the signal argument types, the future output type has to be inferred from /// the call to this function. - pub fn to_fallible_future(&self) -> FallibleSignalFuture { + pub fn to_fallible_future(&self) -> FallibleSignalFuture { FallibleSignalFuture::new(self.clone()) } @@ -312,12 +313,12 @@ impl Signal { /// /// Since the `Signal` type does not contain information on the signal argument types, the future output type has to be inferred from /// the call to this function. - pub fn to_future(&self) -> SignalFuture { + pub fn to_future(&self) -> SignalFuture { SignalFuture::new(self.clone()) } } -impl TypedSignal<'_, C, R> { +impl TypedSignal<'_, C, R> { /// Creates a fallible future for this signal. /// /// The future will resolve the next time the signal is emitted. @@ -335,7 +336,7 @@ impl TypedSignal<'_, C, R> { } } -impl IntoFuture for &TypedSignal<'_, C, R> { +impl IntoFuture for &TypedSignal<'_, C, R> { type Output = R; type IntoFuture = SignalFuture; @@ -349,7 +350,7 @@ impl IntoFuture for &TypedSigna /// /// This allows to turn any implementor into a type that is `Send`, but requires to also implement [`DynamicSend`] as well. /// The later trait will verify if a value can actually be sent between threads at runtime. -pub trait IntoDynamicSend: Sealed { +pub trait IntoDynamicSend: Sealed + 'static { type Target: DynamicSend; fn into_dynamic_send(self) -> Self::Target; diff --git a/godot-macros/src/class/data_models/func.rs b/godot-macros/src/class/data_models/func.rs index a5b2a73f5..0a91f7e50 100644 --- a/godot-macros/src/class/data_models/func.rs +++ b/godot-macros/src/class/data_models/func.rs @@ -58,7 +58,8 @@ pub fn make_virtual_callback( let wrapped_method = make_forwarding_closure(class_name, signature_info, before_kind, interface_trait); - let sig_tuple = signature_info.tuple_type(); + let sig_params = signature_info.params_type(); + let sig_ret = &signature_info.return_type; let call_ctx = make_call_context( class_name.to_string().as_str(), @@ -69,7 +70,8 @@ pub fn make_virtual_callback( quote! { { use ::godot::sys; - type Sig = #sig_tuple; + type CallParams = #sig_params; + type CallRet = #sig_ret; unsafe extern "C" fn virtual_fn( instance_ptr: sys::GDExtensionClassInstancePtr, @@ -94,7 +96,8 @@ pub fn make_method_registration( interface_trait: Option<&venial::TypeExpr>, ) -> ParseResult { let signature_info = &func_definition.signature_info; - let sig_tuple = signature_info.tuple_type(); + let sig_params = signature_info.params_type(); + let sig_ret = &signature_info.return_type; let is_script_virtual = func_definition.is_script_virtual; let method_flags = match make_method_flags(signature_info.receiver_type, is_script_virtual) { @@ -137,7 +140,8 @@ pub fn make_method_registration( use ::godot::builtin::{StringName, Variant}; use ::godot::sys; - type Sig = #sig_tuple; + type CallParams = #sig_params; + type CallRet = #sig_ret; let method_name = StringName::from(#method_name_str); @@ -146,7 +150,7 @@ pub fn make_method_registration( // SAFETY: varcall_fn + ptrcall_fn interpret their in/out parameters correctly. let method_info = unsafe { - ClassMethodInfo::from_signature::<#class_name, Sig>( + ClassMethodInfo::from_signature::<#class_name, CallParams, CallRet>( method_name, Some(varcall_fn), Some(ptrcall_fn), @@ -209,12 +213,9 @@ impl SignatureInfo { } } - // The below functions share quite a bit of tokenization. If ever we run into codegen slowness, we could cache/reuse identical - // sub-expressions. - - pub fn tuple_type(&self) -> TokenStream { - // Note: for GdSelf receivers, first parameter is not even part of SignatureInfo anymore. - util::make_signature_tuple_type(&self.return_type, &self.param_types) + pub fn params_type(&self) -> TokenStream { + let param_types = &self.param_types; + quote! { (#(#param_types,)*) } } } @@ -544,7 +545,7 @@ fn make_ptrcall_invocation(wrapped_method: &TokenStream, is_virtual: bool) -> To }; quote! { - ::in_ptrcall( + ::godot::meta::Signature::::in_ptrcall( instance_ptr, &call_ctx, args_ptr, @@ -558,7 +559,7 @@ fn make_ptrcall_invocation(wrapped_method: &TokenStream, is_virtual: bool) -> To /// Generate code for a `varcall()` call expression. fn make_varcall_invocation(wrapped_method: &TokenStream) -> TokenStream { quote! { - ::in_varcall( + ::godot::meta::Signature::::in_varcall( instance_ptr, &call_ctx, args_ptr, diff --git a/godot-macros/src/class/data_models/inherent_impl.rs b/godot-macros/src/class/data_models/inherent_impl.rs index cc6d96af5..eba6a3fb7 100644 --- a/godot-macros/src/class/data_models/inherent_impl.rs +++ b/godot-macros/src/class/data_models/inherent_impl.rs @@ -422,7 +422,8 @@ fn add_virtual_script_call( }; let method_name_cstr = c_str(&method_name_str); - let sig_tuple = signature_info.tuple_type(); + let call_params = signature_info.params_type(); + let call_ret = &signature_info.return_type; let arg_names = &signature_info.param_idents; let (object_ptr, receiver); @@ -442,10 +443,11 @@ fn add_virtual_script_call( if has_virtual_override { // Dynamic dispatch. - type CallSig = #sig_tuple; + type CallParams = #call_params; + type CallRet = #call_ret; let args = (#( #arg_names, )*); unsafe { - ::out_script_virtual_call( + ::godot::meta::Signature::::out_script_virtual_call( #class_name_str, #method_name_str, method_sname_ptr, diff --git a/godot-macros/src/class/data_models/signal.rs b/godot-macros/src/class/data_models/signal.rs index 5064f43d1..6ab368722 100644 --- a/godot-macros/src/class/data_models/signal.rs +++ b/godot-macros/src/class/data_models/signal.rs @@ -230,15 +230,15 @@ fn make_signal_registration(details: &SignalDetails, class_name_obj: &TokenStrea .. } = details; - let signature_tuple = util::make_signature_tuple_type("e! { () }, param_types); + let param_list = util::make_signature_param_type(param_types); let indexes = 0..param_types.len(); let param_property_infos = quote! { [ // Don't use raw sys pointers directly; it's very easy to have objects going out of scope. #( - <#signature_tuple as ::godot::meta::VarcallSignatureTuple> - ::param_property_info(#indexes, #param_names_str), + <#param_list as ::godot::meta::ParamTuple> + ::property_info(#indexes, #param_names_str).unwrap(), )* ] }; diff --git a/godot-macros/src/util/mod.rs b/godot-macros/src/util/mod.rs index be3081e4d..d10a51a01 100644 --- a/godot-macros/src/util/mod.rs +++ b/godot-macros/src/util/mod.rs @@ -107,13 +107,10 @@ pub fn parse_signature(mut signature: TokenStream) -> venial::Function { reduce_to_signature(&function_item) } -/// Returns a type expression that can be used as a `VarcallSignatureTuple`. -pub fn make_signature_tuple_type( - ret_type: &TokenStream, - param_types: &[venial::TypeExpr], -) -> TokenStream { +/// Returns a type expression that can be used as a `ParamTuple`. +pub fn make_signature_param_type(param_types: &[venial::TypeExpr]) -> TokenStream { quote::quote! { - (#ret_type, #(#param_types),*) + (#(#param_types,)*) } }