Skip to main content

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