@@ -11,7 +11,7 @@ use crate::models::domain::{ExtensionApi, ModName, NativeStructure, TyName};
11
11
use crate :: util:: ident;
12
12
use crate :: { conv, util, SubmitFn } ;
13
13
use proc_macro2:: TokenStream ;
14
- use quote:: quote;
14
+ use quote:: { format_ident , quote} ;
15
15
use std:: path:: Path ;
16
16
17
17
pub fn generate_native_structures_files (
@@ -52,8 +52,15 @@ pub fn generate_native_structures_files(
52
52
53
53
#[ derive( Clone , Eq , PartialEq , Debug ) ]
54
54
pub ( crate ) struct NativeStructuresField {
55
+ /// Identifier for the field name, e.g. `collider`.
55
56
pub field_name : String ,
57
+
58
+ /// Type which has a raw format that is latter mapped to `RustTy`.
59
+ ///
60
+ /// Corresponds to other Godot type names, e.g. `Object*` or `enum::TextServer.Direction`.
56
61
pub field_type : String ,
62
+
63
+ /// If the field is an array, this contains the number of elements.
57
64
pub array_size : Option < usize > ,
58
65
}
59
66
@@ -64,13 +71,20 @@ fn make_native_structure(
64
71
) -> builtins:: GeneratedBuiltin {
65
72
let class_name = & class_name. rust_ty ;
66
73
74
+ // TODO for native structures holding object pointers, we should encapsulate the raw object, as follows:
75
+ // - Make raw object pointer field private (and maybe also ID field, to keep in sync).
76
+ // - Add constructor that takes Gd<Object> instead of ID/raw-ptr fields.
77
+ // - Add getter and setter methods with Gd<Object> (already present).
78
+ // - Add Drop impl, which decrements refcount if the constructor was used, and does nothing if FromGodot pointer conversion was used.
79
+
67
80
let imports = util:: make_imports ( ) ;
68
- let fields = make_native_structure_fields ( & structure. format , ctx) ;
81
+ let ( fields, methods ) = make_native_structure_fields_and_methods ( structure, ctx) ;
69
82
let doc = format ! ( "[`ToGodot`] and [`FromGodot`] are implemented for `*mut {class_name}` and `*const {class_name}`." ) ;
70
83
71
84
// mod re_export needed, because class should not appear inside the file module, and we can't re-export private struct as pub
72
85
let tokens = quote ! {
73
86
#imports
87
+ use std:: ffi:: c_void; // for opaque object pointer fields
74
88
use crate :: meta:: { GodotConvert , FromGodot , ToGodot } ;
75
89
76
90
/// Native structure; can be passed via pointer in APIs that are not exposed to GDScript.
@@ -82,6 +96,10 @@ fn make_native_structure(
82
96
#fields
83
97
}
84
98
99
+ impl #class_name {
100
+ #methods
101
+ }
102
+
85
103
impl GodotConvert for * mut #class_name {
86
104
type Via = i64 ;
87
105
}
@@ -119,25 +137,36 @@ fn make_native_structure(
119
137
builtins:: GeneratedBuiltin { code : tokens }
120
138
}
121
139
122
- fn make_native_structure_fields ( format_str : & str , ctx : & mut Context ) -> TokenStream {
123
- let fields = parse_native_structures_format ( format_str)
140
+ fn make_native_structure_fields_and_methods (
141
+ structure : & NativeStructure ,
142
+ ctx : & mut Context ,
143
+ ) -> ( TokenStream , TokenStream ) {
144
+ let fields = parse_native_structures_format ( & structure. format )
124
145
. expect ( "Could not parse native_structures format field" ) ;
125
146
126
- let field_definitions = fields
127
- . into_iter ( )
128
- . map ( |field| make_native_structure_field_definition ( field, ctx) ) ;
147
+ let mut field_definitions = vec ! [ ] ;
148
+ let mut accessors = vec ! [ ] ;
129
149
130
- quote ! {
131
- #( #field_definitions ) *
150
+ for field in fields {
151
+ let ( field_def, accessor) = make_native_structure_field_and_accessor ( field, ctx) ;
152
+ field_definitions. push ( field_def) ;
153
+ if let Some ( accessor) = accessor {
154
+ accessors. push ( accessor) ;
155
+ }
132
156
}
157
+
158
+ let fields = quote ! { #( #field_definitions ) * } ;
159
+ let methods = quote ! { #( #accessors ) * } ;
160
+
161
+ ( fields, methods)
133
162
}
134
163
135
- fn make_native_structure_field_definition (
164
+ fn make_native_structure_field_and_accessor (
136
165
field : NativeStructuresField ,
137
166
ctx : & mut Context ,
138
- ) -> TokenStream {
167
+ ) -> ( TokenStream , Option < TokenStream > ) {
139
168
let field_type = normalize_native_structure_field_type ( & field. field_type ) ;
140
- let field_type = conv:: to_rust_type_abi ( & field_type, ctx) ;
169
+ let ( field_type, is_object_ptr ) = conv:: to_rust_type_abi ( & field_type, ctx) ;
141
170
142
171
// Make array if needed.
143
172
let field_type = if let Some ( size) = field. array_size {
@@ -146,11 +175,57 @@ fn make_native_structure_field_definition(
146
175
quote ! { #field_type }
147
176
} ;
148
177
149
- let field_name = ident ( & conv:: to_snake_case ( & field. field_name ) ) ;
178
+ let snake_field_name = ident ( & conv:: to_snake_case ( & field. field_name ) ) ;
179
+
180
+ let ( field_name, accessor) ;
181
+ if is_object_ptr {
182
+ // Highlight that the pointer field is internal/opaque.
183
+ field_name = format_ident ! ( "raw_{}_ptr" , snake_field_name) ;
184
+
185
+ // Generate method that converts from instance ID.
186
+ let getter_name = format_ident ! ( "get_{}" , snake_field_name) ;
187
+ let setter_name = format_ident ! ( "set_{}" , snake_field_name) ;
188
+ let id_field_name = format_ident ! ( "{}_id" , snake_field_name) ;
189
+
190
+ // Current native structures treat all object pointers as Object (even if concrete ones like `collider` might be Node).
191
+ // Having node also avoids the lifetime issue with reference-counted objects (unclear if gdext should increment refcount or not).
192
+ // If other fields use different classes in the future, we'll need to update this.
193
+ accessor = Some ( quote ! {
194
+ /// Returns the object as a `Gd<Node>`, or `None` if it no longer exists.
195
+ pub fn #getter_name( & self ) -> Option <Gd <Object >> {
196
+ crate :: obj:: InstanceId :: try_from_u64( self . #id_field_name. id)
197
+ . and_then( |id| Gd :: try_from_instance_id( id) . ok( ) )
198
+
199
+ // Sanity check for consistency (if Some(...)):
200
+ // let ptr = self.#field_name as sys::GDExtensionObjectPtr;
201
+ // unsafe { Gd::from_obj_sys(ptr) }
202
+ }
203
+
204
+ /// Sets the object from a `Gd` pointer holding `Node` or a derived class.
205
+ pub fn #setter_name<T >( & mut self , #snake_field_name: Gd <T >)
206
+ where T : crate :: obj:: Inherits <Object > {
207
+ use crate :: meta:: GodotType as _;
150
208
151
- quote ! {
209
+ let obj = #snake_field_name. upcast( ) ;
210
+
211
+ assert!( obj. is_instance_valid( ) , "provided node is dead" ) ;
212
+
213
+ let id = obj. instance_id( ) . to_u64( ) ;
214
+
215
+ self . #id_field_name = ObjectId { id } ;
216
+ self . #field_name = obj. obj_sys( ) as * mut std:: ffi:: c_void;
217
+ }
218
+ } ) ;
219
+ } else {
220
+ field_name = snake_field_name;
221
+ accessor = None ;
222
+ } ;
223
+
224
+ let field_def = quote ! {
152
225
pub #field_name: #field_type,
153
- }
226
+ } ;
227
+
228
+ ( field_def, accessor)
154
229
}
155
230
156
231
fn normalize_native_structure_field_type ( field_type : & str ) -> String {
0 commit comments