Skip to content

Commit 76b2d99

Browse files
authored
Merge pull request #840 from godot-rust/qol/export-codegen
Rewrite `#[var]` + `#[export]` registration to use type-safe API behind scenes
2 parents 6101ea0 + 9dc7038 commit 76b2d99

File tree

7 files changed

+88
-82
lines changed

7 files changed

+88
-82
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (c) godot-rust; Bromeon and contributors.
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
//! Internal registration machinery used by proc-macro APIs.
9+
10+
use crate::builtin::StringName;
11+
use crate::global::PropertyUsageFlags;
12+
use crate::meta::{ClassName, GodotConvert, GodotType, PropertyHintInfo, PropertyInfo};
13+
use crate::obj::GodotClass;
14+
use crate::registry::property::Var;
15+
use crate::sys;
16+
use godot_ffi::GodotFfi;
17+
18+
pub fn register_var_or_export<C: GodotClass, T: Var>(
19+
property_name: &str,
20+
getter_name: &str,
21+
setter_name: &str,
22+
hint_info: PropertyHintInfo,
23+
usage: PropertyUsageFlags,
24+
) {
25+
let info = PropertyInfo {
26+
variant_type: <<T as GodotConvert>::Via as GodotType>::Ffi::variant_type(),
27+
class_name: <T as GodotConvert>::Via::class_name(),
28+
property_name: StringName::from(property_name),
29+
hint_info,
30+
usage,
31+
};
32+
33+
let class_name = C::class_name();
34+
35+
register_var_or_export_inner(info, class_name, getter_name, setter_name);
36+
}
37+
38+
fn register_var_or_export_inner(
39+
info: PropertyInfo,
40+
class_name: ClassName,
41+
getter_name: &str,
42+
setter_name: &str,
43+
) {
44+
let getter_name = StringName::from(getter_name);
45+
let setter_name = StringName::from(setter_name);
46+
47+
let property_info_sys = info.property_sys();
48+
49+
unsafe {
50+
sys::interface_fn!(classdb_register_extension_class_property)(
51+
sys::get_library(),
52+
class_name.string_sys(),
53+
std::ptr::addr_of!(property_info_sys),
54+
setter_name.string_sys(),
55+
getter_name.string_sys(),
56+
);
57+
}
58+
}

godot-core/src/registry/method.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use sys::interface_fn;
1111
use crate::builtin::{StringName, Variant};
1212
use crate::global::MethodFlags;
1313
use crate::meta::{ClassName, PropertyInfo, VarcallSignatureTuple};
14+
use crate::obj::GodotClass;
1415

1516
/// Info relating to an argument or return type in a method.
1617
pub struct MethodParamOrReturnInfo {
@@ -52,14 +53,13 @@ impl ClassMethodInfo {
5253
/// `call_func` and `ptrcall_func`, if provided, must:
5354
///
5455
/// - Follow the behavior expected from the `method_flags`.
55-
pub unsafe fn from_signature<S: VarcallSignatureTuple>(
56-
class_name: ClassName,
56+
pub unsafe fn from_signature<C: GodotClass, S: VarcallSignatureTuple>(
5757
method_name: StringName,
5858
call_func: sys::GDExtensionClassMethodCall,
5959
ptrcall_func: sys::GDExtensionClassMethodPtrCall,
6060
method_flags: MethodFlags,
6161
param_names: &[&str],
62-
default_arguments: Vec<Variant>,
62+
// default_arguments: Vec<Variant>, - not yet implemented
6363
) -> Self {
6464
let return_value = S::return_info();
6565
let mut arguments = Vec::new();
@@ -79,13 +79,14 @@ impl ClassMethodInfo {
7979
}))
8080
}
8181

82+
let default_arguments = vec![]; // not yet implemented.
8283
assert!(
8384
default_arguments.len() <= arguments.len(),
8485
"cannot have more default arguments than arguments"
8586
);
8687

8788
Self {
88-
class_name,
89+
class_name: C::class_name(),
8990
method_name,
9091
call_func,
9192
ptrcall_func,

godot-core/src/registry/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ pub mod constant;
1414
pub mod method;
1515
pub mod plugin;
1616
pub mod property;
17+
18+
#[doc(hidden)]
19+
pub mod godot_register_wrappers;

godot-macros/src/class/data_models/func.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -118,20 +118,16 @@ pub fn make_method_registration(
118118
#varcall_fn_decl;
119119
#ptrcall_fn_decl;
120120

121-
// SAFETY:
122-
// `get_varcall_func` upholds all the requirements for `call_func`.
123-
// `get_ptrcall_func` upholds all the requirements for `ptrcall_func`
121+
// SAFETY: varcall_fn + ptrcall_fn interpret their in/out parameters correctly.
124122
let method_info = unsafe {
125-
ClassMethodInfo::from_signature::<Sig>(
126-
#class_name::class_name(),
123+
ClassMethodInfo::from_signature::<#class_name, Sig>(
127124
method_name,
128125
Some(varcall_fn),
129126
Some(ptrcall_fn),
130127
#method_flags,
131128
&[
132129
#( #param_ident_strs ),*
133130
],
134-
Vec::new()
135131
)
136132
};
137133

godot-macros/src/class/data_models/property.rs

+19-62
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
//! Parsing the `var` and `export` attributes on fields.
99
1010
use crate::class::{Field, FieldVar, Fields, GetSet, GetterSetterImpl, UsageFlags};
11-
use crate::util;
1211
use proc_macro2::{Ident, TokenStream};
1312
use quote::quote;
1413

@@ -38,8 +37,6 @@ impl FieldHint {
3837
}
3938

4039
pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
41-
let class_name_obj = util::class_name_obj(class_name);
42-
4340
let mut getter_setter_impls = Vec::new();
4441
let mut export_tokens = Vec::new();
4542

@@ -66,8 +63,6 @@ pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
6663
continue;
6764
};
6865

69-
let field_variant_type = util::property_variant_type(field_type);
70-
let field_class_name = util::property_variant_class_name(field_type);
7166
let field_name = field_ident.to_string();
7267

7368
let FieldVar {
@@ -104,47 +99,32 @@ pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
10499
let hint = match hint {
105100
FieldHint::Inferred => {
106101
if let Some(export_hint) = export_hint {
107-
quote! {
108-
{
109-
let ::godot::meta::PropertyHintInfo { hint, hint_string } = #export_hint;
110-
(hint, hint_string)
111-
}
112-
}
102+
quote! { #export_hint }
113103
} else if export.is_some() {
114-
quote! {
115-
{
116-
let export_hint = <#field_type as ::godot::register::property::Export>::export_hint();
117-
(export_hint.hint, export_hint.hint_string)
118-
}
119-
}
104+
quote! { <#field_type as ::godot::register::property::Export>::export_hint() }
120105
} else {
121-
quote! {
122-
{
123-
let export_hint = <#field_type as ::godot::register::property::Var>::var_hint();
124-
(export_hint.hint, export_hint.hint_string)
125-
}
126-
}
106+
quote! { <#field_type as ::godot::register::property::Var>::var_hint() }
127107
}
128108
}
129109
FieldHint::Hint(hint) => {
130110
let hint_string = if let Some(export_hint) = export_hint {
131111
quote! { #export_hint.hint_string }
132112
} else {
133-
quote! { :godot::builtin::GString::new() }
113+
quote! { ::godot::builtin::GString::new() }
134114
};
135115

136116
quote! {
137-
(
138-
::godot::global::PropertyHint::#hint,
139-
#hint_string
140-
)
117+
::godot::meta::PropertyHintInfo {
118+
hint: ::godot::global::PropertyHint::#hint,
119+
hint_string: #hint_string,
120+
}
141121
}
142122
}
143123
FieldHint::HintWithString { hint, hint_string } => quote! {
144-
(
145-
::godot::global::PropertyHint::#hint,
146-
::godot::builtin::GString::from(#hint_string)
147-
)
124+
::godot::meta::PropertyHintInfo {
125+
hint: ::godot::global::PropertyHint::#hint,
126+
hint_string: ::godot::builtin::GString::from(#hint_string),
127+
}
148128
},
149129
};
150130

@@ -160,36 +140,13 @@ pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
160140
);
161141

162142
export_tokens.push(quote! {
163-
use ::godot::sys::GodotFfi;
164-
165-
let (hint, hint_string) = #hint;
166-
let usage = #usage_flags;
167-
168-
let property_info = ::godot::meta::PropertyInfo {
169-
variant_type: #field_variant_type,
170-
class_name: #field_class_name,
171-
property_name: #field_name.into(),
172-
hint_info: ::godot::meta::PropertyHintInfo {
173-
hint,
174-
hint_string,
175-
},
176-
usage,
177-
};
178-
179-
let getter_name = ::godot::builtin::StringName::from(#getter_name);
180-
let setter_name = ::godot::builtin::StringName::from(#setter_name);
181-
182-
let property_info_sys = property_info.property_sys();
183-
184-
unsafe {
185-
::godot::sys::interface_fn!(classdb_register_extension_class_property)(
186-
::godot::sys::get_library(),
187-
#class_name_obj.string_sys(),
188-
std::ptr::addr_of!(property_info_sys),
189-
setter_name.string_sys(),
190-
getter_name.string_sys(),
191-
);
192-
}
143+
::godot::register::private::register_var_or_export::<#class_name, #field_type>(
144+
#field_name,
145+
#getter_name,
146+
#setter_name,
147+
#hint,
148+
#usage_flags,
149+
);
193150
});
194151
}
195152

godot-macros/src/util/mod.rs

-10
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,6 @@ pub fn class_name_obj(class: &impl ToTokens) -> TokenStream {
3232
quote! { <#class as ::godot::obj::GodotClass>::class_name() }
3333
}
3434

35-
pub fn property_variant_type(property_type: &impl ToTokens) -> TokenStream {
36-
let property_type = property_type.to_token_stream();
37-
quote! { <<#property_type as ::godot::meta::GodotConvert>::Via as ::godot::meta::GodotType>::Ffi::variant_type() }
38-
}
39-
40-
pub fn property_variant_class_name(property_type: &impl ToTokens) -> TokenStream {
41-
let property_type = property_type.to_token_stream();
42-
quote! { <<#property_type as ::godot::meta::GodotConvert>::Via as ::godot::meta::GodotType>::class_name() }
43-
}
44-
4535
pub fn bail_fn<R, T>(msg: impl AsRef<str>, tokens: T) -> ParseResult<R>
4636
where
4737
T: Spanned,

godot/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ pub mod register {
178178
/// Re-exports used by proc-macro API.
179179
#[doc(hidden)]
180180
pub mod private {
181+
pub use godot_core::registry::godot_register_wrappers::*;
181182
pub use godot_core::registry::{constant, method};
182183
}
183184
}

0 commit comments

Comments
 (0)