diff --git a/godot-core/src/builtin/meta/signature.rs b/godot-core/src/builtin/meta/signature.rs index a7c935999..c759ed639 100644 --- a/godot-core/src/builtin/meta/signature.rs +++ b/godot-core/src/builtin/meta/signature.rs @@ -22,16 +22,7 @@ pub trait SignatureTuple { args_ptr: *const sys::GDExtensionConstVariantPtr, ret: sys::GDExtensionVariantPtr, err: *mut sys::GDExtensionCallError, - func: fn(&C, Self::Params) -> Self::Ret, - method_name: &str, - ); - - unsafe fn varcall_mut( - instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *const sys::GDExtensionConstVariantPtr, - ret: sys::GDExtensionVariantPtr, - err: *mut sys::GDExtensionCallError, - func: fn(&mut C, Self::Params) -> Self::Ret, + func: fn(sys::GDExtensionClassInstancePtr, Self::Params) -> Self::Ret, method_name: &str, ); @@ -41,18 +32,7 @@ pub trait SignatureTuple { instance_ptr: sys::GDExtensionClassInstancePtr, args_ptr: *const sys::GDExtensionConstTypePtr, ret: sys::GDExtensionTypePtr, - func: fn(&C, Self::Params) -> Self::Ret, - method_name: &str, - call_type: sys::PtrcallType, - ); - - // 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 ptrcall_mut( - instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *const sys::GDExtensionConstTypePtr, - ret: sys::GDExtensionTypePtr, - func: fn(&mut C, Self::Params) -> Self::Ret, + func: fn(sys::GDExtensionClassInstancePtr, Self::Params) -> Self::Ret, method_name: &str, call_type: sys::PtrcallType, ); @@ -127,133 +107,104 @@ macro_rules! impl_signature_for_tuple { #[inline] unsafe fn varcall( - instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *const sys::GDExtensionConstVariantPtr, - ret: sys::GDExtensionVariantPtr, - err: *mut sys::GDExtensionCallError, - func: fn(&C, Self::Params) -> Self::Ret, - method_name: &str, - ) { - $crate::out!("varcall: {}", method_name); - - let storage = unsafe { crate::private::as_storage::(instance_ptr) }; - let instance = storage.get(); - - let args = ( $( - { - let variant = unsafe { &*(*args_ptr.offset($n) as *mut Variant) }; // TODO from_var_sys - let arg = <$Pn as FromVariant>::try_from_variant(variant) - .unwrap_or_else(|e| param_error::<$Pn>(method_name, $n, variant)); - - arg - }, - )* ); - - let ret_val = func(&*instance, args); - let ret_variant = <$R as ToVariant>::to_variant(&ret_val); // TODO write_sys - unsafe { - *(ret as *mut Variant) = ret_variant; - (*err).error = sys::GDEXTENSION_CALL_OK; - } - } - - #[inline] - unsafe fn varcall_mut( - instance_ptr: sys::GDExtensionClassInstancePtr, + instance_ptr: sys::GDExtensionClassInstancePtr, args_ptr: *const sys::GDExtensionConstVariantPtr, ret: sys::GDExtensionVariantPtr, err: *mut sys::GDExtensionCallError, - func: fn(&mut C, Self::Params) -> Self::Ret, + func: fn(sys::GDExtensionClassInstancePtr, Self::Params) -> Self::Ret, method_name: &str, ) { - $crate::out!("varcall: {}", method_name); - - let storage = unsafe { crate::private::as_storage::(instance_ptr) }; - let mut instance = storage.get_mut(); + $crate::out!("varcall: {}", method_name); - let args = ( $( - { - let variant = unsafe { &*(*args_ptr.offset($n) as *mut Variant) }; // TODO from_var_sys - let arg = <$Pn as FromVariant>::try_from_variant(variant) - .unwrap_or_else(|e| param_error::<$Pn>(method_name, $n, variant)); + let args = ($( + unsafe { varcall_arg::<$Pn, $n>(args_ptr, method_name) }, + )*) ; - arg - }, - )* ); - - let ret_val = func(&mut *instance, args); - let ret_variant = <$R as ToVariant>::to_variant(&ret_val); // TODO write_sys - unsafe { - *(ret as *mut Variant) = ret_variant; - (*err).error = sys::GDEXTENSION_CALL_OK; - } + varcall_return::<$R>(func(instance_ptr, args), ret, err) } #[inline] unsafe fn ptrcall( - instance_ptr: sys::GDExtensionClassInstancePtr, + instance_ptr: sys::GDExtensionClassInstancePtr, args_ptr: *const sys::GDExtensionConstTypePtr, ret: sys::GDExtensionTypePtr, - func: fn(&C, Self::Params) -> Self::Ret, + func: fn(sys::GDExtensionClassInstancePtr, Self::Params) -> Self::Ret, method_name: &str, call_type: sys::PtrcallType, ) { $crate::out!("ptrcall: {}", method_name); - let storage = unsafe { crate::private::as_storage::(instance_ptr) }; - let instance = storage.get(); - - let args = ( $( - unsafe { - <$Pn as sys::GodotFuncMarshal>::try_from_arg( - sys::force_mut_ptr(*args_ptr.offset($n)), - call_type - ) - } - .unwrap_or_else(|e| param_error::<$Pn>(method_name, $n, &e)), - )* ); + let args = ($( + unsafe { ptrcall_arg::<$Pn, $n>(args_ptr, method_name, call_type) }, + )*) ; - let ret_val = func(&*instance, args); // SAFETY: // `ret` is always a pointer to an initialized value of type $R // TODO: double-check the above - <$R as sys::GodotFuncMarshal>::try_return(ret_val, ret, call_type) - .unwrap_or_else(|ret_val| return_error::<$R>(method_name, &ret_val)); + ptrcall_return::<$R>(func(instance_ptr, args), ret, method_name, call_type) } + } + }; +} - #[inline] - unsafe fn ptrcall_mut( - instance_ptr: sys::GDExtensionClassInstancePtr, - args_ptr: *const sys::GDExtensionConstTypePtr, - ret: sys::GDExtensionTypePtr, - func: fn(&mut C, Self::Params) -> Self::Ret, - method_name: &str, - call_type: sys::PtrcallType, - ) { - $crate::out!("ptrcall: {}", method_name); +/// 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, + method_name: &str, +) -> P { + let variant = &*(*args_ptr.offset(N) as *mut Variant); // TODO from_var_sys + P::try_from_variant(variant) + .unwrap_or_else(|_| param_error::

(method_name, N as i32, variant)) +} - let storage = unsafe { crate::private::as_storage::(instance_ptr) }; - let mut instance = storage.get_mut(); +/// Moves `ret_val` into `ret`. +/// +/// # Safety +/// - `ret` must be a pointer to an initialized `Variant`. +/// - It must be safe to write a `Variant` once to `ret`. +/// - It must be safe to write a `sys::GDExtensionCallError` once to `err`. +unsafe fn varcall_return( + ret_val: R, + ret: sys::GDExtensionConstVariantPtr, + err: *mut sys::GDExtensionCallError, +) { + let ret_variant = ret_val.to_variant(); // TODO write_sys + *(ret as *mut Variant) = ret_variant; + (*err).error = sys::GDEXTENSION_CALL_OK; +} - let args = ( $( - unsafe { - <$Pn as sys::GodotFuncMarshal>::try_from_arg( - sys::force_mut_ptr(*args_ptr.offset($n)), - call_type - ) - } - .unwrap_or_else(|e| param_error::<$Pn>(method_name, $n, &e)), - )* ); +/// 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, + method_name: &str, + call_type: sys::PtrcallType, +) -> P { + P::try_from_arg(sys::force_mut_ptr(*args_ptr.offset(N)), call_type) + .unwrap_or_else(|e| param_error::

(method_name, N as i32, &e)) +} - let ret_val = func(&mut *instance, args); - // SAFETY: - // `ret` is always a pointer to an initialized value of type $R - // TODO: double-check the above - <$R as sys::GodotFuncMarshal>::try_return(ret_val, ret, call_type) - .unwrap_or_else(|ret_val| return_error::<$R>(method_name, &ret_val)); - } - } - }; +/// Moves `ret_val` into `ret`. +/// +/// # Safety +/// `ret_val`, `ret`, and `call_type` must follow the safety requirements as laid out in +/// [`GodotFuncMarshal::try_return`](sys::GodotFuncMarshal::try_return). +unsafe fn ptrcall_return( + ret_val: R, + ret: sys::GDExtensionTypePtr, + method_name: &str, + call_type: sys::PtrcallType, +) { + ret_val + .try_return(ret, call_type) + .unwrap_or_else(|ret_val| return_error::(method_name, &ret_val)) } fn param_error

(method_name: &str, index: i32, arg: &impl Debug) -> ! { diff --git a/godot-core/src/macros.rs b/godot-core/src/macros.rs index 7d961183b..e748b95f7 100644 --- a/godot-core/src/macros.rs +++ b/godot-core/src/macros.rs @@ -37,14 +37,113 @@ macro_rules! gdext_is_not_unit { }; } +#[doc(hidden)] +#[macro_export] +macro_rules! gdext_method_flags { + (ref) => { + $crate::sys::GDEXTENSION_METHOD_FLAGS_DEFAULT + }; + (mut) => { + $crate::sys::GDEXTENSION_METHOD_FLAGS_DEFAULT + }; + (static) => { + $crate::sys::GDEXTENSION_METHOD_FLAG_STATIC + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! gdext_call_signature_method { + ( + ptrcall, + $Class:ty, + $instance_ptr:ident, $args:ident, $ret:ident, + $func:expr, + $method_name:ident, + $ptrcall_type:path + ) => { + ::ptrcall::<$Class>( + $instance_ptr, + $args, + $ret, + $func, + stringify!($method_name), + $ptrcall_type, + ); + }; + + ( + varcall, + $Class:ty, + $instance_ptr:ident, $args:ident, $ret:ident, $err:ident, + $func:expr, + $method_name:ident + ) => { + ::varcall::<$Class>( + $instance_ptr, + $args, + $ret, + $err, + $func, + stringify!($method_name), + ); + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! gdext_wrap_with_unpacked_params { + ( + ref, + $Class:ty, + $method_name:ident, + $( $param:ident, )* + ) => { + |instance_ptr, params| { + let ( $($param,)* ) = params; + + let storage = unsafe { $crate::private::as_storage::<$Class>(instance_ptr) }; + let instance = storage.get(); + + instance.$method_name( $( $param, )* ) + } + }; + + ( + mut, + $Class:ty, + $method_name:ident, + $( $param:ident, )* + ) => { + |instance_ptr, params| { + let ( $($param,)* ) = params; + + let storage = unsafe { $crate::private::as_storage::<$Class>(instance_ptr) }; + let mut instance = storage.get_mut(); + + instance.$method_name( $( $param, )* ) + } + }; + + ( + static, + $Class:ty, + $method_name:ident, + $( $param:ident, )* + ) => { + |_, params| { + let ( $($param,)* ) = params; + <$Class>::$method_name( $( $param, )* ) + } + }; +} + #[doc(hidden)] #[macro_export] macro_rules! gdext_register_method_inner { ( - $ptrcall:ident, - $varcall:ident, + $receiver_type:ident, $Class:ty, - $map_method:ident, fn $method_name:ident( $( $param:ident : $ParamTy:ty, )* $( #[opt] $opt_param:ident : $OptParamTy:ty, )* @@ -71,16 +170,12 @@ macro_rules! gdext_register_method_inner { let success = $crate::private::handle_panic( || stringify!($method_name), || { - ::$varcall::< $Class >( - instance_ptr, - args, - ret, - err, - |inst, params| { - let ( $($param,)* ) = params; - inst.$method_name( $( $param, )* ) - }, - stringify!($method_name), + $crate::gdext_call_signature_method!( + varcall, + $Class, + instance_ptr, args, ret, err, + $crate::gdext_wrap_with_unpacked_params!($receiver_type, $Class, $method_name, $($param,)*), + $method_name ); } ); @@ -105,15 +200,12 @@ macro_rules! gdext_register_method_inner { let success = $crate::private::handle_panic( || stringify!($method_name), || { - ::$ptrcall::< $Class >( - instance_ptr, - args, - ret, - |inst, params| { - let ( $($param,)* ) = params; - inst.$method_name( $( $param, )* ) - }, - stringify!($method_name), + $crate::gdext_call_signature_method!( + ptrcall, + $Class, + instance_ptr, args, ret, + $crate::gdext_wrap_with_unpacked_params!($receiver_type, $Class, $method_name, $($param,)*), + $method_name, sys::PtrcallType::Standard ); } @@ -163,7 +255,7 @@ macro_rules! gdext_register_method_inner { method_userdata: std::ptr::null_mut(), call_func: Some(varcall_func), ptrcall_func: Some(ptrcall_func), - method_flags: sys::GDEXTENSION_METHOD_FLAGS_DEFAULT as u32, + method_flags: $crate::gdext_method_flags!($receiver_type) as u32, has_return_value: has_return_value as u8, return_value_info: std::ptr::addr_of_mut!(return_value_info_sys), return_value_metadata, @@ -194,6 +286,26 @@ macro_rules! gdext_register_method_inner { #[doc(hidden)] #[macro_export] macro_rules! gdext_register_method { + // immutable + ( + $Class:ty, + fn $method_name:ident( + &self + $(, $arg:ident : $Param:ty)* + $(, #[opt] $opt_arg:ident : $OptParam:ty)* + $(,)? + ) -> $RetTy:ty + ) => { + $crate::gdext_register_method_inner!( + ref, + $Class, + fn $method_name( + $( $arg : $Param, )* + $( #[opt] $opt_arg : $OptParam, )* + ) -> $RetTy + ) + }; + // mutable ( $Class:ty, @@ -205,10 +317,8 @@ macro_rules! gdext_register_method { ) -> $RetTy:ty ) => { $crate::gdext_register_method_inner!( - ptrcall_mut, - varcall_mut, + mut, $Class, - map_mut, fn $method_name( $( $param : $ParamTy, )* $( #[opt] $opt_param : $OptParamTy, )* @@ -216,21 +326,18 @@ macro_rules! gdext_register_method { ) }; - // immutable + // static ( $Class:ty, fn $method_name:ident( - &self - $(, $arg:ident : $Param:ty)* + $( $arg:ident : $Param:ty),* $(, #[opt] $opt_arg:ident : $OptParam:ty)* $(,)? ) -> $RetTy:ty ) => { $crate::gdext_register_method_inner!( - ptrcall, - varcall, + static, $Class, - map, fn $method_name( $( $arg : $Param, )* $( #[opt] $opt_arg : $OptParam, )* @@ -238,6 +345,27 @@ macro_rules! gdext_register_method { ) }; + // immutable without return type + ( + $Class:ty, + fn $method_name:ident( + &self + $(, $param:ident : $ParamTy:ty )* + $(, #[opt] $opt_param:ident : $OptParamTy:ty )* + $(,)? + ) + ) => { + // recurse this macro + $crate::gdext_register_method!( + $Class, + fn $method_name( + &self, + $( $param : $ParamTy, )* + $( #[opt] $opt_param : $OptParamTy, )* + ) -> () + ) + }; + // mutable without return type ( $Class:ty, @@ -259,12 +387,11 @@ macro_rules! gdext_register_method { ) }; - // immutable without return type + // static without return type ( $Class:ty, fn $method_name:ident( - &self - $(, $param:ident : $ParamTy:ty )* + $( $param:ident : $ParamTy:ty ),* $(, #[opt] $opt_param:ident : $OptParamTy:ty )* $(,)? ) @@ -273,7 +400,6 @@ macro_rules! gdext_register_method { $crate::gdext_register_method!( $Class, fn $method_name( - &self, $( $param : $ParamTy, )* $( #[opt] $opt_param : $OptParamTy, )* ) -> () @@ -315,52 +441,54 @@ macro_rules! gdext_virtual_method_callback_inner { /// Returns a C function which acts as the callback when a virtual method of this instance is invoked. // // Note: code duplicated with gdext_virtual_method_callback +// There are currently no virtual static methods. Additionally, virtual static methods dont really make a lot +// of sense. Therefore there is no need to support them. #[doc(hidden)] #[macro_export] macro_rules! gdext_virtual_method_callback { - // mutable + // immutable ( $Class:ty, fn $method_name:ident( - &mut self + &self $(, $param:ident : $ParamTy:ty)* $(,)? ) -> $RetTy:ty ) => { $crate::gdext_virtual_method_callback_inner!( - ptrcall_mut, + ref, $Class, - map_mut, + map, fn $method_name( $( $param : $ParamTy, )* ) -> $RetTy ) }; - // immutable + // mutable ( $Class:ty, fn $method_name:ident( - &self + &mut self $(, $param:ident : $ParamTy:ty)* $(,)? ) -> $RetTy:ty ) => { $crate::gdext_virtual_method_callback_inner!( - ptrcall, + mut, $Class, - map, + map_mut, fn $method_name( $( $param : $ParamTy, )* ) -> $RetTy ) }; - // mutable without return type + // immutable without return type ( $Class:ty, fn $method_name:ident( - &mut self + &self $(, $param:ident : $ParamTy:ty)* $(,)? ) @@ -369,17 +497,17 @@ macro_rules! gdext_virtual_method_callback { $crate::gdext_virtual_method_callback!( $Class, fn $method_name( - &mut self + &self $(, $param : $ParamTy )* ) -> () ) }; - // immutable without return type + // mutable without return type ( $Class:ty, fn $method_name:ident( - &self + &mut self $(, $param:ident : $ParamTy:ty)* $(,)? ) @@ -388,7 +516,7 @@ macro_rules! gdext_virtual_method_callback { $crate::gdext_virtual_method_callback!( $Class, fn $method_name( - &self + &mut self $(, $param : $ParamTy )* ) -> () ) @@ -398,21 +526,21 @@ macro_rules! gdext_virtual_method_callback { #[macro_export] macro_rules! gdext_ptrcall { ( - $ptrcall:ident; + $receiver_type:ident; $instance_ptr:ident, $args_ptr:ident, $ret_ptr:ident; $Class:ty; fn $method_name:ident( $( $arg:ident : $ParamTy:ty, )* ) -> $( $RetTy:tt )+ ) => { - use $crate::builtin::meta::SignatureTuple; - <($($RetTy)+, $($ParamTy,)*) as SignatureTuple>::$ptrcall::<$Class>( - $instance_ptr, - $args_ptr, - $ret_ptr, - |__instance, ($($arg,)*)| __instance.$method_name($($arg,)*), - stringify!($method_name), - sys::PtrcallType::Virtual, - ) + type Sig = ($($RetTy)+, $($ParamTy,)*); + $crate::gdext_call_signature_method!( + ptrcall, + $Class, + $instance_ptr, $args_ptr, $ret_ptr, + $crate::gdext_wrap_with_unpacked_params!($receiver_type, $Class, $method_name, $($arg,)*), + $method_name, + $crate::sys::PtrcallType::Virtual + ); }; } diff --git a/itest/godot/ManualFfiTests.gd b/itest/godot/ManualFfiTests.gd index 73b56f8f9..398f2684e 100644 --- a/itest/godot/ManualFfiTests.gd +++ b/itest/godot/ManualFfiTests.gd @@ -144,4 +144,9 @@ func test_refcounted_as_object_return_from_user_func_varcall(): func test_refcounted_as_object_return_from_user_func_ptrcall(): var obj_test: ObjectTest = ObjectTest.new() var obj: MockRefCountedRust = obj_test.return_refcounted_as_object() - assert_eq(obj.i, 42) \ No newline at end of file + assert_eq(obj.i, 42) + +func test_custom_constructor(): + var obj = CustomConstructor.construct_object(42) + assert_eq(obj.val, 42) + obj.free() \ No newline at end of file diff --git a/itest/godot/input/GenFfiTests.template.gd b/itest/godot/input/GenFfiTests.template.gd index 6a820b089..8b4a0faaa 100644 --- a/itest/godot/input/GenFfiTests.template.gd +++ b/itest/godot/input/GenFfiTests.template.gd @@ -21,6 +21,16 @@ func test_varcall_IDENT(): assert_eq(mirrored, from_gdscript, "mirrored == from_gdscript") #) +#( +func test_varcall_static_IDENT(): + var from_rust: Variant = GenFfi.return_static_IDENT() + assert_that(GenFfi.accept_static_IDENT(from_rust), "ffi.accept_static_IDENT(from_rust)") + + var from_gdscript: Variant = VAL + var mirrored: Variant = GenFfi.mirror_static_IDENT(from_gdscript) + assert_eq(mirrored, from_gdscript, "mirrored_static == from_gdscript") +#) + #( func test_ptrcall_IDENT(): var ffi := GenFfi.new() @@ -32,3 +42,14 @@ func test_ptrcall_IDENT(): var mirrored: TYPE = ffi.mirror_IDENT(from_gdscript) assert_eq(mirrored, from_gdscript, "mirrored == from_gdscript") #) + +#( +func test_ptrcall_static_IDENT(): + var from_rust: TYPE = GenFfi.return_static_IDENT() + assert_that(GenFfi.accept_static_IDENT(from_rust), "ffi.accept_static_IDENT(from_rust)") + + var from_gdscript: TYPE = VAL + var mirrored: TYPE = GenFfi.mirror_static_IDENT(from_gdscript) + assert_eq(mirrored, from_gdscript, "mirrored_static == from_gdscript") +#) + \ No newline at end of file diff --git a/itest/rust/build.rs b/itest/rust/build.rs index 2b21ca60b..9059e84bb 100644 --- a/itest/rust/build.rs +++ b/itest/rust/build.rs @@ -172,6 +172,9 @@ fn generate_rust_methods(inputs: &[Input]) -> Vec { let return_method = format_ident!("return_{}", ident); let accept_method = format_ident!("accept_{}", ident); let mirror_method = format_ident!("mirror_{}", ident); + let return_static_method = format_ident!("return_static_{}", ident); + let accept_static_method = format_ident!("accept_static_{}", ident); + let mirror_static_method = format_ident!("mirror_static_{}", ident); quote! { #[func] @@ -188,6 +191,21 @@ fn generate_rust_methods(inputs: &[Input]) -> Vec { fn #mirror_method(&self, i: #rust_ty) -> #rust_ty { i } + + #[func] + fn #return_static_method() -> #rust_ty { + #rust_val + } + + #[func] + fn #accept_static_method(i: #rust_ty) -> bool { + i == #rust_val + } + + #[func] + fn #mirror_static_method(i: #rust_ty) -> #rust_ty { + i + } } }) .collect() diff --git a/itest/rust/src/object_test.rs b/itest/rust/src/object_test.rs index 7495ffe07..00b633440 100644 --- a/itest/rust/src/object_test.rs +++ b/itest/rust/src/object_test.rs @@ -761,6 +761,33 @@ pub mod object_test_gd { Gd::new(MockRefCountedRust { i: 42 }).upcast() } } + + // ---------------------------------------------------------------------------------------------------------------------------------------------- + + #[derive(GodotClass)] + #[class(base=Object)] + pub struct CustomConstructor { + #[base] + base: Base, + + #[export] + pub val: i64, + } + + #[godot_api] + impl CustomConstructor { + #[func] + pub fn construct_object(val: i64) -> Gd { + Gd::with_base(|base| Self { base, val }) + } + } +} + +#[itest] +fn custom_constructor_works() { + let obj = object_test_gd::CustomConstructor::construct_object(42); + assert_eq!(obj.bind().val, 42); + obj.free(); } // ----------------------------------------------------------------------------------------------------------------------------------------------