@@ -6,9 +6,9 @@ use proc_macro2::TokenStream;
6
6
use quote:: { format_ident, quote} ;
7
7
8
8
use std:: collections:: HashMap ;
9
- use std:: collections:: HashSet ;
10
9
11
10
/// Types of icalls.
11
+ #[ derive( Copy , Clone , PartialEq , Eq , Hash , Debug ) ]
12
12
pub ( crate ) enum IcallType {
13
13
Ptr ,
14
14
Varargs ,
@@ -265,6 +265,14 @@ pub(crate) fn generate_methods(
265
265
icalls : & mut HashMap < String , MethodSig > ,
266
266
docs : Option < & GodotXmlDocs > ,
267
267
) -> TokenStream {
268
+ /// Memorized information about generated methods. Used to generate indexed property accessors.
269
+ struct Generated {
270
+ icall : proc_macro2:: Ident ,
271
+ icall_ty : IcallType ,
272
+ maybe_unsafe : TokenStream ,
273
+ maybe_unsafe_reason : & ' static str ,
274
+ }
275
+
268
276
// Brings values of some types to a type with less information.
269
277
fn arg_erase ( ty : & Ty , name : & proc_macro2:: Ident ) -> TokenStream {
270
278
match ty {
@@ -280,11 +288,14 @@ pub(crate) fn generate_methods(
280
288
281
289
Ty :: Object ( _) => quote ! { #name. as_arg_ptr( ) } ,
282
290
291
+ // Allow lossy casting of numeric types into similar primitives, see also to_return_post
292
+ Ty :: I64 | Ty :: F64 | Ty :: Bool => quote ! { #name as _ } ,
293
+
283
294
_ => quote ! { #name } ,
284
295
}
285
296
}
286
297
287
- let mut method_set = HashSet :: new ( ) ;
298
+ let mut generated = HashMap :: new ( ) ;
288
299
let mut result = TokenStream :: new ( ) ;
289
300
290
301
for method in & class. methods {
@@ -301,11 +312,9 @@ pub(crate) fn generate_methods(
301
312
let mut rust_ret_type = ret_type. to_rust ( ) ;
302
313
303
314
// Ensure that methods are not injected several times.
304
- let method_name_string = method_name. to_string ( ) ;
305
- if method_set. contains ( & method_name_string) {
315
+ if generated. contains_key ( method_name) {
306
316
continue ;
307
317
}
308
- method_set. insert ( method_name_string) ;
309
318
310
319
let mut params_decl = TokenStream :: new ( ) ;
311
320
let mut params_use = TokenStream :: new ( ) ;
@@ -387,7 +396,126 @@ pub(crate) fn generate_methods(
387
396
}
388
397
}
389
398
} ;
399
+
390
400
result. extend ( output) ;
401
+
402
+ generated. insert (
403
+ method_name. to_string ( ) ,
404
+ Generated {
405
+ icall,
406
+ icall_ty,
407
+ maybe_unsafe,
408
+ maybe_unsafe_reason,
409
+ } ,
410
+ ) ;
411
+ }
412
+
413
+ for property in & class. properties {
414
+ if property. index < 0 || property. name . contains ( '/' ) {
415
+ continue ;
416
+ }
417
+
418
+ let property_index = property. index ;
419
+ let ty = Ty :: from_src ( & property. type_ ) ;
420
+
421
+ if let Some ( Generated {
422
+ icall,
423
+ icall_ty,
424
+ maybe_unsafe,
425
+ maybe_unsafe_reason,
426
+ } ) = generated. get ( & property. getter )
427
+ {
428
+ let rusty_name = rust_safe_name ( & property. name ) ;
429
+ let rust_ret_type = ty. to_rust ( ) ;
430
+
431
+ let method_bind_fetch = {
432
+ let method_table = format_ident ! ( "{}MethodTable" , class. name) ;
433
+ let rust_method_name = format_ident ! ( "{}" , property. getter) ;
434
+
435
+ quote ! {
436
+ let method_bind: * mut sys:: godot_method_bind = #method_table:: get( get_api( ) ) . #rust_method_name;
437
+ }
438
+ } ;
439
+
440
+ let doc_comment = docs
441
+ . and_then ( |docs| {
442
+ docs. get_class_method_desc (
443
+ class. name . as_str ( ) ,
444
+ & format ! ( "get_{}" , property. name) ,
445
+ )
446
+ } )
447
+ . unwrap_or ( "" ) ;
448
+
449
+ let recover = ret_recover ( & ty, * icall_ty) ;
450
+
451
+ let output = quote ! {
452
+ #[ doc = #doc_comment]
453
+ #[ doc = #maybe_unsafe_reason]
454
+ #[ inline]
455
+ pub #maybe_unsafe fn #rusty_name( & self ) -> #rust_ret_type {
456
+ unsafe {
457
+ #method_bind_fetch
458
+
459
+ let ret = crate :: icalls:: #icall( method_bind, self . this. sys( ) . as_ptr( ) , #property_index) ;
460
+
461
+ #recover
462
+ }
463
+ }
464
+ } ;
465
+
466
+ result. extend ( output) ;
467
+ }
468
+
469
+ if let Some ( Generated {
470
+ icall,
471
+ icall_ty,
472
+ maybe_unsafe,
473
+ maybe_unsafe_reason,
474
+ } ) = generated. get ( & property. setter )
475
+ {
476
+ let rusty_name = rust_safe_name ( & format ! ( "set_{}" , property. name) ) ;
477
+
478
+ let rust_arg_ty = ty. to_rust_arg ( ) ;
479
+ let arg_ident = format_ident ! ( "value" ) ;
480
+ let arg_erased = arg_erase ( & ty, & arg_ident) ;
481
+
482
+ let method_bind_fetch = {
483
+ let method_table = format_ident ! ( "{}MethodTable" , class. name) ;
484
+ let rust_method_name = format_ident ! ( "{}" , property. setter) ;
485
+
486
+ quote ! {
487
+ let method_bind: * mut sys:: godot_method_bind = #method_table:: get( get_api( ) ) . #rust_method_name;
488
+ }
489
+ } ;
490
+
491
+ let doc_comment = docs
492
+ . and_then ( |docs| {
493
+ docs. get_class_method_desc (
494
+ class. name . as_str ( ) ,
495
+ & format ! ( "set_{}" , property. name) ,
496
+ )
497
+ } )
498
+ . unwrap_or ( "" ) ;
499
+
500
+ let recover = ret_recover ( & Ty :: Void , * icall_ty) ;
501
+
502
+ let output = quote ! {
503
+ #[ doc = #doc_comment]
504
+ #[ doc = #maybe_unsafe_reason]
505
+ #[ inline]
506
+ pub #maybe_unsafe fn #rusty_name( & self , #arg_ident: #rust_arg_ty) {
507
+ unsafe {
508
+ #method_bind_fetch
509
+
510
+ let ret = crate :: icalls:: #icall( method_bind, self . this. sys( ) . as_ptr( ) , #property_index, #arg_erased) ;
511
+
512
+ #recover
513
+ }
514
+ }
515
+ } ;
516
+
517
+ result. extend ( output) ;
518
+ }
391
519
}
392
520
393
521
result
0 commit comments