godot_core/registry/
method.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
8use godot_ffi as sys;
9use sys::interface_fn;
10
11use crate::builtin::{StringName, Variant};
12use crate::global::MethodFlags;
13use crate::meta::{ClassId, GodotConvert, GodotType, ParamTuple, PropertyInfo, Signature};
14use crate::obj::GodotClass;
15
16/// Info relating to an argument or return type in a method.
17pub struct MethodParamOrReturnInfo {
18    pub(crate) info: PropertyInfo,
19    metadata: sys::GDExtensionClassMethodArgumentMetadata,
20}
21
22impl MethodParamOrReturnInfo {
23    pub fn new(info: PropertyInfo, metadata: sys::GDExtensionClassMethodArgumentMetadata) -> Self {
24        Self { info, metadata }
25    }
26}
27
28/// All info needed to register a method for a class with Godot.
29pub struct ClassMethodInfo {
30    class_id: ClassId,
31    method_name: StringName,
32    call_func: sys::GDExtensionClassMethodCall,
33    ptrcall_func: sys::GDExtensionClassMethodPtrCall,
34    method_flags: MethodFlags,
35    return_value: Option<MethodParamOrReturnInfo>,
36    arguments: Vec<MethodParamOrReturnInfo>,
37    /// Whether default arguments are real "arguments" is controversial. From the function PoV they are, but for the caller,
38    /// they are just pre-set values to fill in for missing arguments.
39    default_arguments: Vec<Variant>,
40}
41
42impl ClassMethodInfo {
43    /// # Safety
44    ///
45    /// `ptrcall_func`, if provided, must:
46    ///
47    /// - Interpret its parameters according to the types specified in `S`.
48    /// - Return the value that is specified in `S`, or return nothing if the return value is `()`.
49    ///
50    /// `call_func`, if provided, must:
51    ///
52    /// - Interpret its parameters as a list of `S::PARAM_COUNT` `Variant`s.
53    /// - Return a `Variant`.
54    ///
55    /// `call_func` and `ptrcall_func`, if provided, must:
56    ///
57    /// - Follow the behavior expected from the `method_flags`.
58    pub unsafe fn from_signature<C: GodotClass, Params: ParamTuple, Ret: GodotConvert>(
59        method_name: StringName,
60        call_func: sys::GDExtensionClassMethodCall,
61        ptrcall_func: sys::GDExtensionClassMethodPtrCall,
62        method_flags: MethodFlags,
63        param_names: &[&str],
64        default_arguments: Vec<Variant>,
65    ) -> Self {
66        let return_value = Ret::Via::return_info();
67        let arguments = Signature::<Params, Ret>::param_names(param_names);
68
69        assert!(
70            default_arguments.len() <= arguments.len(),
71            "cannot have more default arguments than arguments"
72        );
73
74        Self {
75            class_id: C::class_id(),
76            method_name,
77            call_func,
78            ptrcall_func,
79            method_flags,
80            return_value,
81            arguments,
82            default_arguments,
83        }
84    }
85
86    pub fn register_extension_class_method(&self) {
87        use crate::obj::EngineBitfield as _;
88
89        let (return_value_info, return_value_metadata) = match &self.return_value {
90            Some(info) => (Some(&info.info), info.metadata),
91            None => (None, 0),
92        };
93
94        let mut return_value_sys = return_value_info
95            .as_ref()
96            .map(|info| info.property_sys())
97            .unwrap_or(PropertyInfo::empty_sys());
98
99        let mut arguments_info_sys: Vec<sys::GDExtensionPropertyInfo> = self
100            .arguments
101            .iter()
102            .map(|argument| argument.info.property_sys())
103            .collect();
104
105        let mut arguments_metadata: Vec<sys::GDExtensionClassMethodArgumentMetadata> =
106            self.arguments.iter().map(|info| info.metadata).collect();
107
108        let mut default_arguments_sys: Vec<sys::GDExtensionVariantPtr> = self
109            .default_arguments
110            .iter()
111            .map(|v| sys::SysPtr::force_mut(v.var_sys()))
112            .collect();
113
114        let method_info_sys = sys::GDExtensionClassMethodInfo {
115            name: sys::SysPtr::force_mut(self.method_name.string_sys()),
116            method_userdata: std::ptr::null_mut(),
117            call_func: self.call_func,
118            ptrcall_func: self.ptrcall_func,
119            method_flags: self.method_flags.ord() as u32,
120            has_return_value: self.return_value.is_some() as u8,
121            return_value_info: std::ptr::addr_of_mut!(return_value_sys),
122            return_value_metadata,
123            argument_count: self.argument_count(),
124            arguments_info: arguments_info_sys.as_mut_ptr(),
125            arguments_metadata: arguments_metadata.as_mut_ptr(),
126            default_argument_count: self.default_argument_count(),
127            default_arguments: default_arguments_sys.as_mut_ptr(),
128        };
129
130        if self.method_flags.is_set(MethodFlags::VIRTUAL) {
131            self.register_virtual_class_method(method_info_sys, return_value_sys);
132        } else {
133            self.register_nonvirtual_class_method(method_info_sys);
134        }
135    }
136
137    fn register_nonvirtual_class_method(&self, method_info_sys: sys::GDExtensionClassMethodInfo) {
138        // SAFETY: The lifetime of the data we use here is at least as long as this function's scope. So we can
139        // safely call this function without issue.
140        //
141        // Null pointers will only be passed along if we indicate to Godot that they are unused.
142        unsafe {
143            interface_fn!(classdb_register_extension_class_method)(
144                sys::get_library(),
145                self.class_id.string_sys(),
146                std::ptr::addr_of!(method_info_sys),
147            )
148        }
149    }
150
151    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
152    fn register_virtual_class_method(
153        &self,
154        normal_method_info: sys::GDExtensionClassMethodInfo,
155        return_value_sys: sys::GDExtensionPropertyInfo, // passed separately because value, not pointer.
156    ) {
157        // Copy everything possible from regular method info.
158        let method_info_sys = sys::GDExtensionClassVirtualMethodInfo {
159            name: normal_method_info.name,
160            method_flags: normal_method_info.method_flags,
161            return_value: return_value_sys,
162            return_value_metadata: normal_method_info.return_value_metadata,
163            argument_count: normal_method_info.argument_count,
164            arguments: normal_method_info.arguments_info,
165            arguments_metadata: normal_method_info.arguments_metadata,
166        };
167
168        // SAFETY: Godot only needs arguments to be alive during the method call.
169        unsafe {
170            interface_fn!(classdb_register_extension_class_virtual_method)(
171                sys::get_library(),
172                self.class_id.string_sys(),
173                std::ptr::addr_of!(method_info_sys),
174            )
175        }
176    }
177
178    // Polyfill doing nothing.
179    #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
180    fn register_virtual_class_method(
181        &self,
182        _normal_method_info: sys::GDExtensionClassMethodInfo,
183        _return_value_sys: sys::GDExtensionPropertyInfo,
184    ) {
185    }
186
187    fn argument_count(&self) -> u32 {
188        self.arguments
189            .len()
190            .try_into()
191            .expect("arguments length should fit in u32")
192    }
193
194    fn default_argument_count(&self) -> u32 {
195        self.default_arguments
196            .len()
197            .try_into()
198            .expect("arguments length should fit in u32")
199    }
200}