@@ -16,8 +16,8 @@ use crate::meta::ClassName;
16
16
use crate :: obj:: { cap, DynGd , Gd , GodotClass } ;
17
17
use crate :: private:: { ClassPlugin , PluginItem } ;
18
18
use crate :: registry:: callbacks;
19
- use crate :: registry:: plugin:: { ErasedDynifyFn , ErasedRegisterFn , InherentImpl } ;
20
- use crate :: { classes , godot_error, godot_warn, sys} ;
19
+ use crate :: registry:: plugin:: { DynTraitImpl , ErasedRegisterFn , ITraitImpl , InherentImpl , Struct } ;
20
+ use crate :: { godot_error, godot_warn, sys} ;
21
21
use sys:: { interface_fn, out, Global , GlobalGuard , GlobalLockError } ;
22
22
23
23
/// Returns a lock to a global map of loaded classes, by initialization level.
@@ -45,9 +45,8 @@ fn global_loaded_classes_by_name() -> GlobalGuard<'static, HashMap<ClassName, Cl
45
45
lock_or_panic ( & LOADED_CLASSES_BY_NAME , "loaded classes (by name)" )
46
46
}
47
47
48
- fn global_dyn_traits_by_typeid (
49
- ) -> GlobalGuard < ' static , HashMap < any:: TypeId , Vec < DynToClassRelation > > > {
50
- static DYN_TRAITS_BY_TYPEID : Global < HashMap < any:: TypeId , Vec < DynToClassRelation > > > =
48
+ fn global_dyn_traits_by_typeid ( ) -> GlobalGuard < ' static , HashMap < any:: TypeId , Vec < DynTraitImpl > > > {
49
+ static DYN_TRAITS_BY_TYPEID : Global < HashMap < any:: TypeId , Vec < DynTraitImpl > > > =
51
50
Global :: default ( ) ;
52
51
53
52
lock_or_panic ( & DYN_TRAITS_BY_TYPEID , "dyn traits" )
@@ -68,12 +67,6 @@ pub struct LoadedClass {
68
67
// Currently empty, but should already work for per-class queries.
69
68
pub struct ClassMetadata { }
70
69
71
- /// Represents a `dyn Trait` implemented (and registered) for a class.
72
- pub struct DynToClassRelation {
73
- implementing_class_name : ClassName ,
74
- erased_dynify_fn : ErasedDynifyFn ,
75
- }
76
-
77
70
// ----------------------------------------------------------------------------------------------------------------------------------------------
78
71
79
72
// This works as long as fields are called the same. May still need individual #[cfg]s for newer fields.
@@ -110,7 +103,7 @@ struct ClassRegistrationInfo {
110
103
is_editor_plugin : bool ,
111
104
112
105
/// One entry for each `dyn Trait` implemented (and registered) for this class.
113
- dynify_fns_by_trait : HashMap < any:: TypeId , ErasedDynifyFn > ,
106
+ dynify_fns_by_trait : HashMap < any:: TypeId , DynTraitImpl > ,
114
107
115
108
/// Used to ensure that each component is only filled once.
116
109
component_already_filled : [ bool ; 4 ] ,
@@ -251,14 +244,11 @@ fn register_classes_and_dyn_traits(
251
244
let metadata = ClassMetadata { } ;
252
245
253
246
// Transpose Class->Trait relations to Trait->Class relations.
254
- for ( trait_type_id, dynify_fn ) in info. dynify_fns_by_trait . drain ( ) {
247
+ for ( trait_type_id, dyn_trait_impl ) in info. dynify_fns_by_trait . drain ( ) {
255
248
dyn_traits_by_typeid
256
249
. entry ( trait_type_id)
257
250
. or_default ( )
258
- . push ( DynToClassRelation {
259
- implementing_class_name : class_name,
260
- erased_dynify_fn : dynify_fn,
261
- } ) ;
251
+ . push ( dyn_trait_impl) ;
262
252
}
263
253
264
254
loaded_classes_by_level
@@ -301,15 +291,16 @@ pub fn auto_register_rpcs<T: GodotClass>(object: &mut T) {
301
291
}
302
292
}
303
293
304
- /// Tries to upgrade a polymorphic `Gd<T>` to `DynGd<T, D>`, where the `T` -> `D` relation is only present via derived objects.
294
+ /// Tries to convert a `Gd<T>` to a `DynGd<T, D>` for some class `T` and trait object `D`, where the trait may only be implemented for
295
+ /// some subclass of `T`.
305
296
///
306
- /// This works without direct `T: AsDyn<D>` because it considers `object`'s dynamic type `Td : Inherits<T >`.
297
+ /// This works even when `T` doesn't implement ` AsDyn<D>`, as long as the dynamic class of `object` implements `AsDyn<D >`.
307
298
///
308
- /// Only direct relations are considered, i.e. the `Td: AsDyn<D>` must be fulfilled (and registered). If any intermediate base class of `Td`
309
- /// implements the trait `D `, this will not consider it. Base-derived conversions are theoretically possible, but need quite a bit of extra
310
- /// machinery.
299
+ /// This only looks for an ` AsDyn<D>` implementation in the dynamic class; the conversion will fail if the dynamic class doesn't
300
+ /// implement `AsDyn<D> `, even if there exists some superclass that does implement `AsDyn<D>`. This restriction could in theory be
301
+ /// lifted, but would need quite a bit of extra machinery to work .
311
302
pub ( crate ) fn try_dynify_object < T : GodotClass , D : ?Sized + ' static > (
312
- object : Gd < T > ,
303
+ mut object : Gd < T > ,
313
304
) -> Result < DynGd < T , D > , ConvertError > {
314
305
let typeid = any:: TypeId :: of :: < D > ( ) ;
315
306
let trait_name = sys:: short_type_name :: < D > ( ) ;
@@ -322,28 +313,16 @@ pub(crate) fn try_dynify_object<T: GodotClass, D: ?Sized + 'static>(
322
313
323
314
// TODO maybe use 2nd hashmap instead of linear search.
324
315
// (probably not pair of typeid/classname, as that wouldn't allow the above check).
325
- let dynamic_class = object. dynamic_class_string ( ) ;
326
-
327
316
for relation in relations {
328
- if dynamic_class == relation. implementing_class_name . to_string_name ( ) {
329
- let erased = ( relation. erased_dynify_fn ) ( object. upcast_object ( ) ) ;
330
-
331
- // Must succeed, or was registered wrong.
332
- let dyn_gd_object = erased. boxed . downcast :: < DynGd < classes:: Object , D > > ( ) ;
333
-
334
- // SAFETY: the relation ensures that the **unified** (for storage) pointer was of type `DynGd<classes::Object, D>`.
335
- let dyn_gd_object = unsafe { dyn_gd_object. unwrap_unchecked ( ) } ;
336
-
337
- // SAFETY: the relation ensures that the **original** pointer was of type `DynGd<T, D>`.
338
- let dyn_gd_t = unsafe { dyn_gd_object. cast_unchecked :: < T > ( ) } ;
339
-
340
- return Ok ( dyn_gd_t) ;
317
+ match relation. get_dyn_gd ( object) {
318
+ Ok ( dyn_gd) => return Ok ( dyn_gd) ,
319
+ Err ( obj) => object = obj,
341
320
}
342
321
}
343
322
344
323
let error = FromGodotError :: UnimplementedDynTrait {
345
324
trait_name,
346
- class_name : dynamic_class . to_string ( ) ,
325
+ class_name : object . dynamic_class_string ( ) . to_string ( ) ,
347
326
} ;
348
327
349
328
Err ( error. into_error ( object) )
@@ -376,8 +355,8 @@ where
376
355
trait_name = sys:: short_type_name:: <D >( )
377
356
) ;
378
357
379
- GString :: from ( join_with ( relations. iter ( ) , ", " , |relation | {
380
- relation . implementing_class_name . to_cow_str ( )
358
+ GString :: from ( join_with ( relations. iter ( ) , ", " , |dyn_trait | {
359
+ dyn_trait . class_name ( ) . to_cow_str ( )
381
360
} ) )
382
361
}
383
362
@@ -388,7 +367,7 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
388
367
// out!("| reg (before): {c:?}");
389
368
// out!("| comp: {component:?}");
390
369
match item {
391
- PluginItem :: Struct {
370
+ PluginItem :: Struct ( Struct {
392
371
base_class_name,
393
372
generated_create_fn,
394
373
generated_recreate_fn,
@@ -401,7 +380,7 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
401
380
is_instantiable,
402
381
#[ cfg( all( since_api = "4 . 3 ", feature = "register-docs") ) ]
403
382
docs : _,
404
- } => {
383
+ } ) => {
405
384
c. parent_class_name = Some ( base_class_name) ;
406
385
c. default_virtual_fn = default_get_virtual_fn;
407
386
c. register_properties_fn = Some ( register_properties_fn) ;
@@ -458,7 +437,7 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
458
437
c. register_methods_constants_fn = Some ( register_methods_constants_fn) ;
459
438
}
460
439
461
- PluginItem :: ITraitImpl {
440
+ PluginItem :: ITraitImpl ( ITraitImpl {
462
441
user_register_fn,
463
442
user_create_fn,
464
443
user_recreate_fn,
@@ -473,7 +452,7 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
473
452
user_property_get_revert_fn,
474
453
#[ cfg( all( since_api = "4 . 3 ", feature = "register-docs") ) ]
475
454
virtual_method_docs : _,
476
- } => {
455
+ } ) => {
477
456
c. user_register_fn = user_register_fn;
478
457
479
458
// The following unwraps of fill_into() shouldn't panic, since rustc will error if there are
@@ -497,20 +476,17 @@ fn fill_class_info(item: PluginItem, c: &mut ClassRegistrationInfo) {
497
476
c. godot_params . free_property_list_func = user_free_property_list_fn;
498
477
c. godot_params . property_can_revert_func = user_property_can_revert_fn;
499
478
c. godot_params . property_get_revert_func = user_property_get_revert_fn;
500
- c. user_virtual_fn = Some ( get_virtual_fn) ;
479
+ c. user_virtual_fn = get_virtual_fn;
501
480
}
502
- PluginItem :: DynTraitImpl {
503
- dyn_trait_typeid,
504
- erased_dynify_fn,
505
- } => {
506
- let prev = c
507
- . dynify_fns_by_trait
508
- . insert ( dyn_trait_typeid, erased_dynify_fn) ;
481
+ PluginItem :: DynTraitImpl ( dyn_trait_impl) => {
482
+ let type_id = dyn_trait_impl. dyn_trait_typeid ( ) ;
483
+
484
+ let prev = c. dynify_fns_by_trait . insert ( type_id, dyn_trait_impl) ;
509
485
510
486
assert ! (
511
487
prev. is_none( ) ,
512
488
"Duplicate registration of {:?} for class {}" ,
513
- dyn_trait_typeid ,
489
+ type_id ,
514
490
c. class_name
515
491
) ;
516
492
}
0 commit comments