ferroui_base/type_system.rs
1//! Runtime type information and the class model of the object hierarchy.
2//!
3//! The framework is a deep single-inheritance class hierarchy with virtual
4//! members. It is modelled as follows:
5//!
6//! * A class is a `#[repr(C)]` struct whose first field, `base`, is its base
7//! class. It derefs to the base, so inherited members are reached through
8//! auto-deref with static dispatch and no runtime cost.
9//! * Objects live on the heap behind [`Ref<T>`], a reference-counted handle
10//! that can be upcast to any base class for free and downcast with a runtime
11//! check, like an object reference in a managed language.
12//! * Virtual members are declared once with [`ferro_class!`]. For each class
13//! that introduces virtuals this generates a `<Class>Impl` trait whose
14//! associated functions are the overridable members. A subclass overrides a
15//! member by implementing the function; members it does not implement fall
16//! through to the nearest base class that does, and an override can call
17//! its base implementation with `parent_<member>`.
18//! * Dispatch goes through one per-class table of function pointers. An
19//! object holds a single pointer to the table of its most-derived class.
20//! * The slot of a member a class does not override holds the implementation
21//! the member falls through to, where the class states what it overrides
22//! ([`ferro_impl_classes!`], [`ferro_overrides!`]); otherwise it holds a
23//! function of the class that forwards to the table of its base class.
24//!
25//! All `unsafe` needed for this lives in this module and in the macros it
26//! exports, and rests on two invariants that the macros establish and check
27//! at compile time: a class struct starts with its base class at offset zero,
28//! and a class's virtual table starts with its base class's virtual table.
29
30use crate::metadata::{MarkupType, TypeOf};
31use crate::{FerroObject, FerroProperty};
32use std::any::{Any, TypeId};
33use std::collections::HashMap;
34use std::fmt;
35use std::hash::{Hash, Hasher};
36use std::marker::PhantomData;
37use std::ops::Deref;
38use std::rc::{Rc, Weak};
39use std::sync::{OnceLock, RwLock};
40
41/// Describes a class (or a non-instantiable type that owns attached
42/// properties): its name, where it is declared, its base class, how to
43/// initialise it and how to create an instance of it.
44///
45/// A `TypeInfo` is plain constant data, built by [`ferro_class!`] and
46/// [`ferro_static_type!`].
47pub struct TypeInfo {
48 name: &'static str,
49 base: Option<&'static TypeInfo>,
50 module_path: &'static str,
51 class_init: Option<fn()>,
52 constructor: Option<fn() -> Ref<FerroObject>>,
53 handles: Option<fn() -> [TypeId; 2]>,
54 handle_registration: Option<fn()>,
55 interfaces: &'static [TypeOf],
56 markup: Option<&'static MarkupType>,
57}
58
59impl TypeInfo {
60 pub const fn new(name: &'static str, base: Option<&'static TypeInfo>) -> Self {
61 Self {
62 name,
63 base,
64 module_path: "",
65 class_init: None,
66 constructor: None,
67 handles: None,
68 handle_registration: None,
69 interfaces: &[],
70 markup: None,
71 }
72 }
73
74 /// States the Rust module that declares the type (`module_path!()` at
75 /// the declaration). The markup-facing [`namespace`](Self::namespace) is
76 /// derived from it.
77 pub const fn with_module_path(mut self, module_path: &'static str) -> Self {
78 self.module_path = module_path;
79 self
80 }
81
82 /// Attaches the static initialisation of the type: a function that,
83 /// once per thread and after doing the same for the base type, registers
84 /// what the type declares (handle casts, property definitions) and runs
85 /// its static constructor.
86 ///
87 /// It is what lets code that only has the runtime type (a lookup of a
88 /// property by owner type and name) initialise a type that has no
89 /// instance yet.
90 pub const fn with_class_init(mut self, class_init: fn()) -> Self {
91 self.class_init = Some(class_init);
92 self
93 }
94
95 /// Attaches the default constructor of the class: the function that
96 /// creates an instance as its parameterless `new()` does.
97 pub const fn with_constructor(mut self, constructor: Option<fn() -> Ref<FerroObject>>) -> Self {
98 self.constructor = constructor;
99 self
100 }
101
102 /// States the Rust types of the handle of the class and of its nullable
103 /// form (`Ref<T>`, `Option<Ref<T>>`), which makes the class findable by
104 /// the type of a value ([`find_by_handle`](Self::find_by_handle)).
105 pub const fn with_handles(mut self, handles: fn() -> [TypeId; 2]) -> Self {
106 self.handles = Some(handles);
107 self
108 }
109
110 /// States the registration of the handle of the class with the untyped
111 /// value conversions
112 /// ([`ValueTypes::register_object`](crate::data::core::ValueTypes::register_object)
113 /// of the class): what makes `Ref<T>` an object handle and
114 /// `Option<Ref<T>>` its nullable form. The initialisation of the class
115 /// makes it on the thread that initialises the class; a class of a table
116 /// of types ([`register_all`](Self::register_all)) has it on every thread,
117 /// initialised or not, because a property of another class may hold the
118 /// null of its handle before anything creates an instance of it.
119 pub const fn with_handle_registration(mut self, handle_registration: fn()) -> Self {
120 self.handle_registration = Some(handle_registration);
121 self
122 }
123
124 /// States the interface handles the class declares
125 /// ([`ferro_class_info!`], `interfaces`).
126 pub const fn with_interfaces(mut self, interfaces: &'static [TypeOf]) -> Self {
127 self.interfaces = interfaces;
128 self
129 }
130
131 /// Attaches the markup metadata of the class ([`ferro_class_info!`],
132 /// `markup`).
133 pub const fn with_markup(mut self, markup: Option<&'static MarkupType>) -> Self {
134 self.markup = markup;
135 self
136 }
137
138 /// The class name, without namespace.
139 #[inline]
140 pub fn name(&self) -> &'static str {
141 self.name
142 }
143
144 /// The direct base class, if any.
145 #[inline]
146 pub fn base_type(&self) -> Option<&'static TypeInfo> {
147 self.base
148 }
149
150 /// The Rust module that declares the type, as `module_path!()` spells
151 /// it (`ferroui_base::media::solid_color_brush`). Empty for a type whose
152 /// runtime type was written by hand.
153 #[inline]
154 pub fn module_path(&self) -> &'static str {
155 self.module_path
156 }
157
158 /// The dotted, markup-facing namespace of the type (`FerroUI.Media`).
159 ///
160 /// Namespaces are declared once per module, not per class: a crate maps
161 /// its module paths to namespaces with [`TypeInfo::register_namespaces`]
162 /// (from its `register_types()` function), and the namespace of a type
163 /// is the one registered for the longest prefix of its
164 /// [`module_path`](Self::module_path). Empty when no prefix is
165 /// registered.
166 pub fn namespace(&self) -> &'static str {
167 let registry = read_registry();
168 registry.namespace_of(self.module_path)
169 }
170
171 /// The dotted namespace registered for a Rust module path; see
172 /// [`namespace`](Self::namespace).
173 pub fn namespace_of_module(module_path: &str) -> &'static str {
174 read_registry().namespace_of(module_path)
175 }
176
177 /// The namespace-qualified name of the type (`FerroUI.Media.Brush`).
178 pub fn full_name(&self) -> String {
179 match self.namespace() {
180 "" => self.name.to_string(),
181 namespace => format!("{namespace}.{}", self.name),
182 }
183 }
184
185 /// Whether the type has a static initialisation of its own
186 /// ([`with_class_init`](Self::with_class_init)).
187 #[cfg(feature = "compiler-metadata")]
188 pub(crate) fn has_class_init(&self) -> bool {
189 self.class_init.is_some()
190 }
191
192 /// Ensures the static initialisation of the type and of its base types
193 /// has run on the current thread: handle casts and property definitions
194 /// are registered (properties in declaration order) and the static
195 /// constructors have run, base types first. Does nothing after the first
196 /// call on a thread.
197 ///
198 /// Called when the first instance of a class is created, when a property
199 /// accessor of the type is first called and by the property registry when
200 /// it is asked about the type; call it directly where the static
201 /// constructor of a type must have run before none of these happened.
202 #[inline]
203 pub fn ensure_class_init(&self) {
204 let mut current = Some(self);
205 while let Some(type_) = current {
206 if let Some(class_init) = type_.class_init {
207 // An initialiser initialises its base types itself.
208 class_init();
209 return;
210 }
211 current = type_.base;
212 }
213 }
214
215 /// The default constructor of the class, if it declares one (see
216 /// [`ferro_class_info!`]): creates an instance as the parameterless
217 /// `new()` of the class does.
218 #[inline]
219 pub fn default_constructor(&self) -> Option<fn() -> Ref<FerroObject>> {
220 self.constructor
221 }
222
223 /// Creates an instance with the default constructor of the class.
224 /// `None` if the class has none (abstract classes, classes that need
225 /// constructor arguments, non-instantiable types).
226 pub fn create_instance(&self) -> Option<Ref<FerroObject>> {
227 self.constructor.map(|constructor| constructor())
228 }
229
230 /// The Rust type of the handle of the class (`Ref<T>`). `None` for types
231 /// that are not classes.
232 pub fn handle(&self) -> Option<TypeId> {
233 self.handles.map(|handles| handles()[0])
234 }
235
236 /// The Rust type of the nullable handle of the class (`Option<Ref<T>>`).
237 pub fn nullable_handle(&self) -> Option<TypeId> {
238 self.handles.map(|handles| handles()[1])
239 }
240
241 /// The interface handles the class itself declares with
242 /// [`ferro_class_info!`] (not the ones of its base classes).
243 #[inline]
244 pub fn interfaces(&self) -> &'static [TypeOf] {
245 self.interfaces
246 }
247
248 /// The markup metadata the class itself declares with
249 /// [`ferro_class_info!`]: its content property, plain properties,
250 /// methods, events and attributes. `None` for a class that declares
251 /// none; the metadata of its base classes still applies to it.
252 #[inline]
253 pub fn markup(&self) -> Option<&'static MarkupType> {
254 self.markup
255 }
256
257 /// The name of the content property of the class: the one it declares,
258 /// or else the one of the nearest base class that declares one.
259 pub fn content_property(&self) -> Option<&'static str> {
260 let mut current = Some(self);
261 while let Some(type_) = current {
262 if let Some(content) = type_.markup.and_then(|m| m.content_property) {
263 return Some(content);
264 }
265 current = type_.base;
266 }
267 None
268 }
269
270 /// The properties registered on the type and its base types (styled,
271 /// direct and the attached properties the type owns), each type's in
272 /// declaration order, most derived type first. Initialises the type if
273 /// needed, so no instance has to exist.
274 pub fn properties(&'static self) -> Rc<Vec<&'static FerroProperty>> {
275 crate::FerroPropertyRegistry::instance().get_registered(self)
276 }
277
278 /// Finds a property registered on the type or its base types by name.
279 /// Initialises the type if needed.
280 pub fn find_property(&'static self, name: &str) -> Option<&'static FerroProperty> {
281 crate::FerroPropertyRegistry::instance().find_registered(self, name)
282 }
283
284 /// Adds a type to the process-wide table of known types, making it
285 /// discoverable with [`find`](Self::find) and
286 /// [`find_by_name`](Self::find_by_name). Registering a type again does
287 /// nothing.
288 ///
289 /// Types register themselves when they are initialised on a thread; a
290 /// crate's `register_types()` function registers all of its types up
291 /// front so that types that were never used are known too.
292 pub fn register(type_: &'static TypeInfo) {
293 if read_registry().contains(type_) {
294 return;
295 }
296 write_registry().insert(type_);
297 // As for the types of a table: see `register_all`.
298 if let Some(handle_registration) = type_.handle_registration {
299 crate::data::core::ValueTypes::register_global(handle_registration);
300 }
301 }
302
303 /// Registers several types; see [`register`](Self::register).
304 ///
305 /// The handle of each class is registered with the untyped value
306 /// conversions of every thread as well
307 /// ([`with_handle_registration`](Self::with_handle_registration)), when
308 /// the thread next converts an untyped value: the nullable form of the
309 /// handle of a class of a table is known whether or not the class has
310 /// been initialised. A property typed with the handle of another class
311 /// holds its null from the start (`Control.ContextMenu`), and what reads
312 /// that value untyped (a binding, markup) must see null. The run-time
313 /// XAML loader initialises a class when it reads it, which hid the
314 /// difference; compiled markup names a class without initialising it.
315 pub fn register_all(types: &[&'static TypeInfo]) {
316 {
317 let mut registry = write_registry();
318 for type_ in types {
319 registry.insert(type_);
320 }
321 }
322 for type_ in types {
323 if let Some(handle_registration) = type_.handle_registration {
324 crate::data::core::ValueTypes::register_global(handle_registration);
325 }
326 }
327 }
328
329 /// Declares the namespaces of a crate's modules: pairs of a Rust module
330 /// path (`"ferroui_base::media"`) and the dotted namespace of the types
331 /// declared in that module and its submodules (`"FerroUI.Media"`). A
332 /// longer module path overrides a shorter one, down to a single file.
333 pub fn register_namespaces(namespaces: &[(&'static str, &'static str)]) {
334 let mut registry = write_registry();
335 for &(module_path, namespace) in namespaces {
336 match registry.namespaces.iter_mut().find(|(m, _)| *m == module_path) {
337 Some(entry) => entry.1 = namespace,
338 None => registry.namespaces.push((module_path, namespace)),
339 }
340 }
341 }
342
343 #[cfg(feature = "compiler-metadata")]
344 /// Declares the public Rust paths of classes: pairs of a class and the
345 /// path another crate names it by (`"ferroui_controls::Border"`). The
346 /// declaring module ([`module_path`](Self::module_path)) is often
347 /// private, so the path cannot be derived from it; a crate records the
348 /// table of its generated `rust_paths.rs` ([`ferro_rust_paths!`](crate::ferro_rust_paths))
349 /// in its `register_types()`. The emitter of Rust source reads them;
350 /// nothing else does.
351 pub fn register_rust_paths(paths: &[(&'static TypeInfo, &'static str)]) {
352 let mut registry = write_registry();
353 for &(type_, path) in paths {
354 registry.rust_paths.insert(type_ as *const TypeInfo as usize, (type_, path));
355 }
356 }
357
358 #[cfg(feature = "compiler-metadata")]
359 /// The public Rust path of the class
360 /// ([`register_rust_paths`](Self::register_rust_paths)), if its crate
361 /// recorded one.
362 pub fn rust_path(&'static self) -> Option<&'static str> {
363 read_registry().rust_paths.get(&(self as *const TypeInfo as usize)).map(|entry| entry.1)
364 }
365
366 #[cfg(feature = "compiler-metadata")]
367 /// Every class with a registered public Rust path, with the path, in no
368 /// particular order.
369 pub fn all_rust_paths() -> Vec<(&'static TypeInfo, &'static str)> {
370 read_registry().rust_paths.values().copied().collect()
371 }
372
373 /// Finds a known type by namespace and name.
374 pub fn find(namespace: &str, name: &str) -> Option<&'static TypeInfo> {
375 let registry = read_registry();
376 let candidates = registry.by_name.get(name)?;
377 candidates.iter().copied().find(|t| registry.namespace_of(t.module_path) == namespace)
378 }
379
380 /// Finds the known class whose handle (`Ref<T>`) or nullable handle
381 /// (`Option<Ref<T>>`) is the Rust type `handle`. The flag tells whether
382 /// it is the nullable handle.
383 pub fn find_by_handle(handle: TypeId) -> Option<(&'static TypeInfo, bool)> {
384 read_registry().by_handle.get(&handle).copied()
385 }
386
387 /// Finds a known type by name alone. `None` if no type or more than one
388 /// type (in different namespaces) has the name.
389 pub fn find_by_name(name: &str) -> Option<&'static TypeInfo> {
390 let registry = read_registry();
391 match registry.by_name.get(name)?.as_slice() {
392 [type_] => Some(type_),
393 _ => None,
394 }
395 }
396
397 /// Every known type, in registration order.
398 pub fn registered_types() -> Vec<&'static TypeInfo> {
399 read_registry().types.clone()
400 }
401
402 /// Returns true if `other` is this type or derives from it.
403 pub fn is_assignable_from(&self, other: &TypeInfo) -> bool {
404 let mut current = Some(other);
405 while let Some(t) = current {
406 if std::ptr::eq(self, t) {
407 return true;
408 }
409 current = t.base;
410 }
411 false
412 }
413
414 /// Returns true if this type is `other` or derives from it.
415 #[inline]
416 pub fn is_subclass_of(&self, other: &TypeInfo) -> bool {
417 other.is_assignable_from(self)
418 }
419}
420
421impl PartialEq for TypeInfo {
422 #[inline]
423 fn eq(&self, other: &Self) -> bool {
424 std::ptr::eq(self, other)
425 }
426}
427
428impl Eq for TypeInfo {}
429
430impl Hash for TypeInfo {
431 fn hash<H: Hasher>(&self, state: &mut H) {
432 (self as *const TypeInfo as usize).hash(state)
433 }
434}
435
436impl fmt::Debug for TypeInfo {
437 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438 f.write_str(self.name)
439 }
440}
441
442impl fmt::Display for TypeInfo {
443 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444 f.write_str(self.name)
445 }
446}
447
448/// The process-wide table of known types. Runtime types are immutable
449/// statics, so unlike the property registry the table is not per thread.
450#[derive(Default)]
451struct TypeRegistry {
452 types: Vec<&'static TypeInfo>,
453 by_name: HashMap<&'static str, Vec<&'static TypeInfo>>,
454 by_handle: HashMap<TypeId, (&'static TypeInfo, bool)>,
455 namespaces: Vec<(&'static str, &'static str)>,
456 /// The public Rust paths of classes, by the address of their type.
457 #[cfg(feature = "compiler-metadata")]
458 rust_paths: HashMap<usize, (&'static TypeInfo, &'static str)>,
459}
460
461impl TypeRegistry {
462 fn contains(&self, type_: &'static TypeInfo) -> bool {
463 self.by_name.get(type_.name).is_some_and(|types| types.iter().any(|t| std::ptr::eq(*t, type_)))
464 }
465
466 fn insert(&mut self, type_: &'static TypeInfo) {
467 if !self.contains(type_) {
468 self.types.push(type_);
469 self.by_name.entry(type_.name).or_default().push(type_);
470 if let Some(handles) = type_.handles {
471 let [handle, nullable] = handles();
472 self.by_handle.insert(handle, (type_, false));
473 self.by_handle.insert(nullable, (type_, true));
474 }
475 }
476 }
477
478 fn namespace_of(&self, module_path: &str) -> &'static str {
479 let mut best: Option<(&'static str, &'static str)> = None;
480 for &(prefix, namespace) in &self.namespaces {
481 let matches = match module_path.strip_prefix(prefix) {
482 Some(rest) => rest.is_empty() || rest.starts_with("::"),
483 None => false,
484 };
485 if matches && best.is_none_or(|(b, _)| prefix.len() > b.len()) {
486 best = Some((prefix, namespace));
487 }
488 }
489 best.map_or("", |(_, namespace)| namespace)
490 }
491}
492
493fn type_registry() -> &'static RwLock<TypeRegistry> {
494 static REGISTRY: OnceLock<RwLock<TypeRegistry>> = OnceLock::new();
495 REGISTRY.get_or_init(Default::default)
496}
497
498fn read_registry() -> std::sync::RwLockReadGuard<'static, TypeRegistry> {
499 type_registry().read().unwrap_or_else(|e| e.into_inner())
500}
501
502fn write_registry() -> std::sync::RwLockWriteGuard<'static, TypeRegistry> {
503 type_registry().write().unwrap_or_else(|e| e.into_inner())
504}
505
506/// A Rust type with an associated runtime [`TypeInfo`].
507///
508/// Implemented by all classes ([`ferro_class!`]) and by non-instantiable
509/// types that own attached properties ([`ferro_static_type!`]).
510pub trait StaticType: 'static {
511 /// The runtime type.
512 const TYPE: &'static TypeInfo;
513}
514
515/// What a type declares when it declares nothing: no default constructor,
516/// no interface casts, no properties and an empty static constructor.
517///
518/// The class macros read the declarations of a type through the names below.
519/// A type that declares one of them does so with an inherent item of the
520/// same name (written by hand for [`static_constructor`](Self::static_constructor),
521/// generated by [`ferro_properties!`](crate::ferro_properties) and
522/// [`ferro_class_info!`] for the others), which takes precedence over this
523/// trait; every other type falls back to it.
524pub trait ClassDefaults {
525 #[doc(hidden)]
526 const __CONSTRUCTOR: Option<fn() -> Ref<FerroObject>> = None;
527
528 #[doc(hidden)]
529 const __INTERFACES: &'static [TypeOf] = &[];
530
531 #[doc(hidden)]
532 const __MARKUP: Option<&'static MarkupType> = None;
533
534 #[doc(hidden)]
535 #[inline]
536 fn __register_interfaces() {}
537
538 #[doc(hidden)]
539 #[inline]
540 fn __register_properties() {}
541
542 /// The static constructor of a type: work done once per thread, after
543 /// the properties of the type are registered and before its first
544 /// instance exists (class handlers, `affects_render`, metadata
545 /// overrides).
546 ///
547 /// A class declares it as an inherent function in the module of its
548 /// `ferro_class!`:
549 ///
550 /// ```ignore
551 /// impl Border {
552 /// fn static_constructor() {
553 /// Self::affects_render::<Border>(&[Self::background_property().as_property()]);
554 /// }
555 /// }
556 /// ```
557 ///
558 /// It is never called by hand: the class initialisation calls it. Use
559 /// `Border::TYPE.ensure_class_init()` to force the initialisation.
560 #[inline]
561 fn static_constructor() {}
562}
563
564impl<T: ?Sized> ClassDefaults for T {}
565
566/// The part of a class initialisation that is the same for every class:
567/// makes the type known by name and registers its handle type `Ref<T>` with
568/// the untyped value conversions (as an object handle that casts to the
569/// handles of its base classes and to its nullable form). The conversions
570/// are registered when the thread first uses untyped conversions.
571#[doc(hidden)]
572#[cold]
573pub fn __register_class<T: ObjectType>() {
574 TypeInfo::register(T::TYPE);
575 crate::data::core::ValueTypes::register_deferred(crate::data::core::ValueTypes::register_object::<T>);
576}
577
578/// Access to the embedded base-class data of an object.
579///
580/// Implemented by [`ferro_class!`] for every ancestor of a class, so
581/// `T: Upcast<U>` reads as "`T` is `U` or derives from it".
582pub trait Upcast<T: ?Sized> {
583 fn upcast(&self) -> &T;
584}
585
586/// Implemented by every class.
587///
588/// # Safety
589///
590/// Implemented only by [`ferro_class!`] (and by hand for the root class).
591/// Implementors guarantee that:
592///
593/// * `Self` is `#[repr(C)]`-compatible with `Parent` at offset zero, i.e. a
594/// pointer to `Self` is a valid pointer to every class in its ancestor
595/// chain;
596/// * `VTable` likewise starts with `Parent::VTable` at offset zero;
597/// * values of `Self` are only created through [`instantiate`] (class structs
598/// have a private `base` field and `construct`-style constructors that are
599/// only meaningful inside `instantiate`).
600pub unsafe trait ObjectType: StaticType + Upcast<FerroObject> + Sized {
601 /// The base class. The root class is its own parent.
602 type Parent: ObjectType;
603
604 /// The virtual table of the class.
605 type VTable: Copy + 'static;
606
607 /// The virtual table used by instances whose most-derived class is `Self`.
608 fn vtable() -> &'static Self::VTable;
609
610 /// The static initialisation of the class. The first call on a thread,
611 /// after doing the same for the base class:
612 ///
613 /// 1. makes the type known by name ([`TypeInfo::find`]);
614 /// 2. registers the handle type `Ref<Self>` and the interface handles the
615 /// class declares ([`ferro_class_info!`]) with the untyped value
616 /// conversions;
617 /// 3. registers the properties of the class in declaration order
618 /// ([`ferro_properties!`](crate::ferro_properties));
619 /// 4. runs the static constructor of the class
620 /// ([`ClassDefaults::static_constructor`]).
621 ///
622 /// Later calls only read a thread-local flag.
623 ///
624 /// Generated by `ferro_class!`. Called by [`instantiate`], so a class
625 /// that has an instance on the thread is initialised, and through
626 /// [`TypeInfo::ensure_class_init`] by property accessors and the property
627 /// registry, for classes that have none.
628 fn register_class();
629}
630
631/// Implemented by a class `C` for every class `T` that can derive from it:
632/// builds `C`'s part of the virtual table with `T`'s overrides.
633pub trait Subclassable<T>: ObjectType {
634 #[doc(hidden)]
635 fn build_vtable() -> Self::VTable;
636}
637
638/// Reinterprets a reference to base class data as a reference to the derived
639/// class `T`.
640///
641/// # Safety
642///
643/// `base` must be the base-class part of an object whose most-derived class
644/// is `T` or derives from `T`.
645#[doc(hidden)]
646#[inline(always)]
647pub unsafe fn cast_this<B, T>(base: &B) -> &T {
648 &*(base as *const B as *const T)
649}
650
651/// Returns the virtual table of `T`'s parent class, viewed as the table type
652/// `V` of the class that introduced the member being called.
653///
654/// Panics if the parent class has no `V`, i.e. when the class that introduced
655/// a virtual member did not implement it itself.
656#[doc(hidden)]
657#[inline]
658pub fn parent_vtable<T: ObjectType, V>() -> &'static V {
659 // A parent class that is an ancestor of the class owning `V` has a
660 // strictly smaller table (the owner adds at least the member being
661 // called), so this check is exact. It is constant per instantiation.
662 assert!(
663 std::mem::size_of::<<T::Parent as ObjectType>::VTable>() >= std::mem::size_of::<V>(),
664 "virtual member has no base implementation"
665 );
666 let table: *const <T::Parent as ObjectType>::VTable = <T::Parent as ObjectType>::vtable();
667 // SAFETY: by the size argument above the parent class is the owner of `V`
668 // or derives from it, and `ObjectType` guarantees that a class's table
669 // starts with the tables of all of its ancestors.
670 unsafe { &*(table as *const V) }
671}
672
673/// Whether the slot of `member` in the table of `T` takes the slot of the
674/// table of `T`'s parent class, instead of a function of `T` that forwards
675/// to it. `V` is the table of the class that introduced the member.
676///
677/// `overrides` is what the implementation of `T` states about itself
678/// (`__OVERRIDES` of the implementation trait): `None` when it states
679/// nothing, else the names of the members it overrides. A member that is
680/// not among them has the default of the implementation trait, which calls
681/// the same slot of the parent table and does nothing else, so the slot of
682/// the parent is the same implementation with one call less.
683///
684/// The slot is not taken where the parent class has no table `V`: the class
685/// that introduced a member has to implement it, and its default panics
686/// when it is called (see [`parent_vtable`]), not when the table is built.
687///
688/// A constant function: the tables evaluate it when the class is compiled,
689/// so a table is built without comparing names and the forwarding function
690/// of a slot that is taken from the parent is not referenced.
691#[doc(hidden)]
692pub const fn __forwards_to_parent<T: ObjectType, V>(
693 overrides: Option<&'static [&'static str]>,
694 member: &str,
695) -> bool {
696 let overrides = match overrides {
697 Some(overrides) => overrides,
698 None => return false,
699 };
700 if std::mem::size_of::<<T::Parent as ObjectType>::VTable>() < std::mem::size_of::<V>() {
701 return false;
702 }
703 let member = member.as_bytes();
704 let mut index = 0;
705 while index < overrides.len() {
706 let name = overrides[index].as_bytes();
707 if name.len() == member.len() {
708 let mut at = 0;
709 while at < name.len() && name[at] == member[at] {
710 at += 1;
711 }
712 if at == name.len() {
713 return false;
714 }
715 }
716 index += 1;
717 }
718 true
719}
720
721/// A strong reference to an object of class `T` or a class derived from it.
722///
723/// Cloning the handle shares the object. Equality and hashing are by object
724/// identity.
725#[repr(transparent)]
726pub struct Ref<T: ObjectType> {
727 ptr: Rc<dyn Any>,
728 _marker: PhantomData<T>,
729}
730
731impl<T: ObjectType> Ref<T> {
732 /// Converts the handle to a handle of a base class.
733 #[inline]
734 pub fn upcast<U: ObjectType>(self) -> Ref<U>
735 where
736 T: Upcast<U>,
737 {
738 Ref { ptr: self.ptr, _marker: PhantomData }
739 }
740
741 /// Borrows the handle as a handle of a base class, without a copy of the
742 /// handle (the borrowing form of [`upcast`](Self::upcast)).
743 #[inline]
744 pub fn upcast_ref<U: ObjectType>(&self) -> &Ref<U>
745 where
746 T: Upcast<U>,
747 {
748 // SAFETY: `Ref` is `repr(transparent)` over its `Rc<dyn Any>`; the
749 // marker is zero-sized, so `Ref<T>` and `Ref<U>` have the same layout
750 // and the same valid values, and `T: Upcast<U>` makes the object a `U`,
751 // which is exactly what `upcast` asserts for an owned handle.
752 unsafe { &*(self as *const Ref<T> as *const Ref<U>) }
753 }
754
755 /// Returns a handle of class `U` if the object is a `U`.
756 #[inline]
757 pub fn cast<U: ObjectType>(&self) -> Option<Ref<U>> {
758 if self.is::<U>() {
759 Some(Ref { ptr: self.ptr.clone(), _marker: PhantomData })
760 } else {
761 None
762 }
763 }
764
765 /// Converts the handle to class `U` if the object is a `U`, otherwise
766 /// returns the handle unchanged.
767 #[inline]
768 pub fn downcast<U: ObjectType>(self) -> Result<Ref<U>, Self> {
769 if self.is::<U>() {
770 Ok(Ref { ptr: self.ptr, _marker: PhantomData })
771 } else {
772 Err(self)
773 }
774 }
775
776 /// Whether the object is of class `U` or a class derived from it.
777 #[inline]
778 pub fn is<U: ObjectType>(&self) -> bool {
779 let object: &FerroObject = (**self).upcast();
780 U::TYPE.is_assignable_from(object.get_type())
781 }
782
783 /// Creates a weak reference to the object.
784 #[inline]
785 pub fn downgrade(&self) -> WeakRef<T> {
786 WeakRef::of(Rc::downgrade(&self.ptr))
787 }
788
789 /// Whether two handles refer to the same object.
790 #[inline]
791 pub fn ptr_eq<U: ObjectType>(&self, other: &Ref<U>) -> bool {
792 std::ptr::addr_eq(Rc::as_ptr(&self.ptr), Rc::as_ptr(&other.ptr))
793 }
794
795 /// The number of handles to the object, the number of weak handles to it and its address:
796 /// what a leak test says of an object that survived its scenario. For tests only.
797 #[cfg(feature = "testing")]
798 #[doc(hidden)]
799 pub fn reference_counts(&self) -> (usize, usize, usize) {
800 (Rc::strong_count(&self.ptr), Rc::weak_count(&self.ptr), Rc::as_ptr(&self.ptr) as *const () as usize)
801 }
802
803 #[inline]
804 pub(crate) fn from_rc(ptr: Rc<dyn Any>) -> Self {
805 Ref { ptr, _marker: PhantomData }
806 }
807}
808
809impl<T: ObjectType> Deref for Ref<T> {
810 type Target = T;
811
812 #[inline]
813 fn deref(&self) -> &T {
814 // SAFETY: a `Ref<T>` is only created by `instantiate` (for the exact
815 // class), by `upcast` (statically an ancestor) and by `cast` /
816 // `downcast` / `FerroObject::to_ref` (after a runtime type check), so
817 // the allocation holds an object whose class is `T` or derives from
818 // it, and `ObjectType` guarantees `T` is at offset zero of it.
819 unsafe { &*(Rc::as_ptr(&self.ptr) as *const T) }
820 }
821}
822
823impl<T: ObjectType> Clone for Ref<T> {
824 #[inline]
825 fn clone(&self) -> Self {
826 Ref { ptr: self.ptr.clone(), _marker: PhantomData }
827 }
828}
829
830impl<T: ObjectType, U: ObjectType> PartialEq<Ref<U>> for Ref<T> {
831 #[inline]
832 fn eq(&self, other: &Ref<U>) -> bool {
833 self.ptr_eq(other)
834 }
835}
836
837impl<T: ObjectType> Eq for Ref<T> {}
838
839impl<T: ObjectType> Hash for Ref<T> {
840 fn hash<H: Hasher>(&self, state: &mut H) {
841 (Rc::as_ptr(&self.ptr) as *const () as usize).hash(state)
842 }
843}
844
845impl<T: ObjectType> fmt::Debug for Ref<T> {
846 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
847 let object: &FerroObject = (**self).upcast();
848 fmt::Debug::fmt(object, f)
849 }
850}
851
852/// The word that follows the pointer of every [`WeakRef`] in a build with the
853/// feature `tagged-weak-references`: a reader of the memory of the process
854/// (the allocation trace of the tests of the ControlCatalog) tells a weak
855/// reference to an object from a strong one by it.
856#[cfg(feature = "tagged-weak-references")]
857pub const WEAK_REFERENCE_TAG: usize = 0x5745_414B_5245_4631_u64 as usize;
858
859/// A weak reference to an object of class `T` or a class derived from it.
860#[cfg_attr(feature = "tagged-weak-references", repr(C))]
861pub struct WeakRef<T: ObjectType> {
862 ptr: Weak<dyn Any>,
863 #[cfg(feature = "tagged-weak-references")]
864 tag: usize,
865 _marker: PhantomData<T>,
866}
867
868impl<T: ObjectType> WeakRef<T> {
869 #[inline]
870 fn of(ptr: Weak<dyn Any>) -> Self {
871 WeakRef {
872 ptr,
873 #[cfg(feature = "tagged-weak-references")]
874 tag: WEAK_REFERENCE_TAG,
875 _marker: PhantomData,
876 }
877 }
878}
879
880impl<T: ObjectType> WeakRef<T> {
881 /// Returns a strong reference if the object is still alive.
882 #[inline]
883 pub fn upgrade(&self) -> Option<Ref<T>> {
884 self.ptr.upgrade().map(|ptr| Ref { ptr, _marker: PhantomData })
885 }
886
887 /// Converts the handle to a handle of a base class.
888 #[inline]
889 pub fn upcast<U: ObjectType>(self) -> WeakRef<U>
890 where
891 T: Upcast<U>,
892 {
893 WeakRef::of(self.ptr)
894 }
895
896 /// Whether the weak reference points at `object`.
897 #[inline]
898 pub fn points_to<U: ObjectType>(&self, object: &Ref<U>) -> bool {
899 std::ptr::addr_eq(self.ptr.as_ptr(), Rc::as_ptr(&object.ptr))
900 }
901
902 /// Whether the weak reference points at `object` (which, being borrowed, is alive).
903 #[inline]
904 pub(crate) fn points_at(&self, object: &T) -> bool {
905 std::ptr::addr_eq(self.ptr.as_ptr(), object as *const T)
906 }
907
908 /// Whether two weak references point at the same object, alive or not.
909 #[inline]
910 pub fn ptr_eq<U: ObjectType>(&self, other: &WeakRef<U>) -> bool {
911 std::ptr::addr_eq(self.ptr.as_ptr(), other.ptr.as_ptr())
912 }
913
914 #[inline]
915 pub(crate) fn from_weak(ptr: Weak<dyn Any>) -> Self {
916 WeakRef::of(ptr)
917 }
918}
919
920impl<T: ObjectType> Clone for WeakRef<T> {
921 #[inline]
922 fn clone(&self) -> Self {
923 WeakRef::of(self.ptr.clone())
924 }
925}
926
927/// An optional object reference accepted by setters, convertible from a
928/// handle (or a borrowed handle) of any derived class and from `None`.
929pub struct Nullable<T: ObjectType>(pub Option<Ref<T>>);
930
931impl<T: ObjectType + Upcast<U>, U: ObjectType> From<Ref<T>> for Nullable<U> {
932 #[inline]
933 fn from(value: Ref<T>) -> Self {
934 Nullable(Some(value.upcast()))
935 }
936}
937
938impl<T: ObjectType + Upcast<U>, U: ObjectType> From<&Ref<T>> for Nullable<U> {
939 #[inline]
940 fn from(value: &Ref<T>) -> Self {
941 Nullable(Some(value.clone().upcast()))
942 }
943}
944
945impl<U: ObjectType> From<Option<Ref<U>>> for Nullable<U> {
946 #[inline]
947 fn from(value: Option<Ref<U>>) -> Self {
948 Nullable(value)
949 }
950}
951
952/// A non-optional object reference accepted by methods, convertible from a
953/// handle (or a borrowed handle) of any derived class.
954pub trait IntoRef<U: ObjectType> {
955 fn into_ref(self) -> Ref<U>;
956}
957
958impl<T: ObjectType + Upcast<U>, U: ObjectType> IntoRef<U> for Ref<T> {
959 #[inline]
960 fn into_ref(self) -> Ref<U> {
961 self.upcast()
962 }
963}
964
965impl<T: ObjectType + Upcast<U>, U: ObjectType> IntoRef<U> for &Ref<T> {
966 #[inline]
967 fn into_ref(self) -> Ref<U> {
968 self.clone().upcast()
969 }
970}
971
972/// Moves a constructed class value to the heap, attaches the virtual table of
973/// its class and runs the `constructed` virtual member. The class (and its
974/// base classes) are initialised first if this is the first instance on the
975/// thread; see [`ObjectType::register_class`].
976pub fn instantiate<T: ObjectType>(value: T) -> Ref<T> {
977 T::register_class();
978 let ptr: Rc<dyn Any> = Rc::new(value);
979 let result: Ref<T> = Ref { ptr, _marker: PhantomData };
980 let object: &FerroObject = (*result).upcast();
981 let table: *const T::VTable = T::vtable();
982 // SAFETY: `T::VTable` starts with the root class's table (`ObjectType`
983 // invariant), and the object's most-derived class is `T`.
984 unsafe { object.attach(Rc::downgrade(&result.ptr), table as *const ()) };
985 object.constructed();
986 result
987}
988
989/// Declares a class.
990///
991/// ```ignore
992/// #[repr(C)]
993/// pub struct Decorator { base: Control, /* fields */ }
994///
995/// // A class without new virtual members:
996/// ferro_class!(Decorator: Control);
997///
998/// // A class that introduces virtual members:
999/// ferro_class! {
1000/// Layoutable: Visual, virtuals LayoutableImpl: VisualImpl {
1001/// /// Measures the control.
1002/// fn measure_override(this, available_size: Size) -> Size;
1003/// fn arrange_override(this, final_size: Size) -> Size;
1004/// }
1005/// }
1006/// ```
1007///
1008/// Both forms generate `Deref<Target = Base>`, [`Upcast`] to every ancestor,
1009/// the `TYPE` runtime type and [`ObjectType`], including the static
1010/// initialisation of the class ([`ObjectType::register_class`]). What the
1011/// initialisation does is taken from the other declarations of the class,
1012/// each optional and found by the macro without being named here:
1013///
1014/// * `ferro_properties! { impl Decorator { .. } }`, the property definitions;
1015/// * `fn static_constructor()`, an inherent function declared in the same
1016/// module ([`ClassDefaults::static_constructor`]);
1017/// * `ferro_class_info!(Decorator { new: Decorator::new, interfaces: [..] })`,
1018/// the default constructor and the interface handles the class converts
1019/// to.
1020///
1021/// The second form also generates:
1022///
1023/// * `LayoutableVTable`, the table of the new members;
1024/// * `trait LayoutableImpl`, with one associated function per member taking
1025/// `this: &Self`. Every class deriving from `Layoutable` implements it
1026/// (`impl LayoutableImpl for Border {}`) and overrides what it needs
1027/// ([`ferro_impl_classes!`] and [`ferro_overrides!`] write the same
1028/// implementation and state what it overrides, which shortens the calls);
1029/// * `LayoutableImplExt::parent_<member>`, to call the base implementation
1030/// from an override;
1031/// * `Layoutable::<member>(&self, ..)`, which performs the virtual call.
1032///
1033/// The declaring class must implement every member itself; these are the
1034/// base implementations.
1035#[macro_export]
1036macro_rules! ferro_class {
1037 // What the type declares, in the order of a static initialisation:
1038 // interface casts, properties (field initialisers), static constructor.
1039 // Inherent items of the type win over the `ClassDefaults` fallbacks.
1040 (@class_members $name:ident) => {{
1041 #[allow(unused_imports)]
1042 use $crate::ClassDefaults as _;
1043 $crate::data::core::ValueTypes::register_deferred(<$name>::__register_interfaces);
1044 <$name>::__register_properties();
1045 <$name>::static_constructor();
1046 }};
1047
1048 (@register_class $name:ident : $base:ident) => {
1049 #[inline]
1050 fn register_class() {
1051 $crate::ferro_thread_local! {
1052 static REGISTERED: ::std::cell::Cell<bool> = const { ::std::cell::Cell::new(false) };
1053 }
1054 #[cold]
1055 #[inline(never)]
1056 fn init() {
1057 <$base as $crate::ObjectType>::register_class();
1058 $crate::__register_class::<$name>();
1059 $crate::ferro_class!(@class_members $name);
1060 }
1061 if !REGISTERED.get() {
1062 // Set first: the initialisation may create instances of the
1063 // class or call its property accessors.
1064 REGISTERED.set(true);
1065 init();
1066 }
1067 }
1068 };
1069
1070 (@common $name:ident : $base:ident) => {
1071 const _: () = assert!(
1072 ::std::mem::offset_of!($name, base) == 0,
1073 "class structs must be #[repr(C)] with `base` as their first field"
1074 );
1075
1076 impl ::std::ops::Deref for $name {
1077 type Target = $base;
1078 #[inline]
1079 fn deref(&self) -> &$base {
1080 &self.base
1081 }
1082 }
1083
1084 impl<T: ?Sized> $crate::Upcast<T> for $name
1085 where
1086 $base: $crate::Upcast<T>,
1087 {
1088 #[inline]
1089 fn upcast(&self) -> &T {
1090 $crate::Upcast::upcast(&self.base)
1091 }
1092 }
1093
1094 impl $crate::Upcast<$name> for $name {
1095 #[inline]
1096 fn upcast(&self) -> &$name {
1097 self
1098 }
1099 }
1100
1101 impl $crate::StaticType for $name {
1102 const TYPE: &'static $crate::TypeInfo = {
1103 static TYPE: $crate::TypeInfo = $crate::TypeInfo::new(
1104 stringify!($name),
1105 Some(<$base as $crate::StaticType>::TYPE),
1106 )
1107 .with_module_path(::std::module_path!())
1108 .with_class_init(<$name as $crate::ObjectType>::register_class)
1109 .with_constructor({
1110 #[allow(unused_imports)]
1111 use $crate::ClassDefaults as _;
1112 <$name>::__CONSTRUCTOR
1113 })
1114 .with_handles(|| {
1115 [
1116 ::std::any::TypeId::of::<$crate::Ref<$name>>(),
1117 ::std::any::TypeId::of::<::std::option::Option<$crate::Ref<$name>>>(),
1118 ]
1119 })
1120 .with_handle_registration($crate::data::core::ValueTypes::register_object::<$name>)
1121 .with_interfaces({
1122 #[allow(unused_imports)]
1123 use $crate::ClassDefaults as _;
1124 <$name>::__INTERFACES
1125 })
1126 .with_markup({
1127 #[allow(unused_imports)]
1128 use $crate::ClassDefaults as _;
1129 <$name>::__MARKUP
1130 });
1131 &TYPE
1132 };
1133 }
1134
1135 impl $name {
1136 /// The runtime type of this class.
1137 pub const TYPE: &'static $crate::TypeInfo = <$name as $crate::StaticType>::TYPE;
1138
1139 /// Returns a strong reference to this object.
1140 #[inline]
1141 pub fn to_ref(&self) -> $crate::Ref<$name> {
1142 $crate::FerroObject::ref_of(self)
1143 }
1144 }
1145 };
1146
1147 ($name:ident : $base:ident) => {
1148 $crate::ferro_class!(@common $name: $base);
1149
1150 // SAFETY: the offset assertion above proves the layout requirement;
1151 // the table type is the parent's.
1152 unsafe impl $crate::ObjectType for $name {
1153 type Parent = $base;
1154 type VTable = <$base as $crate::ObjectType>::VTable;
1155
1156 fn vtable() -> &'static Self::VTable {
1157 static VTABLE: ::std::sync::OnceLock<<$base as $crate::ObjectType>::VTable> =
1158 ::std::sync::OnceLock::new();
1159 VTABLE.get_or_init(<$name as $crate::Subclassable<$name>>::build_vtable)
1160 }
1161
1162 $crate::ferro_class!(@register_class $name: $base);
1163 }
1164
1165 impl<T> $crate::Subclassable<T> for $name
1166 where
1167 $base: $crate::Subclassable<T>,
1168 {
1169 #[inline]
1170 fn build_vtable() -> Self::VTable {
1171 <$base as $crate::Subclassable<T>>::build_vtable()
1172 }
1173 }
1174 };
1175
1176 (
1177 $name:ident : $base:ident, virtuals $impl_trait:ident : $parent_impl:path {
1178 $(
1179 $(#[$meta:meta])*
1180 fn $method:ident(this $(, $arg:ident : $arg_ty:ty)* $(,)?) $(-> $ret:ty)?;
1181 )*
1182 }
1183 ) => {
1184 $crate::ferro_class!(@common $name: $base);
1185
1186 $crate::__paste! {
1187 /// The virtual table of the class.
1188 #[derive(Clone, Copy)]
1189 #[repr(C)]
1190 pub struct [<$name VTable>] {
1191 pub base: <$base as $crate::ObjectType>::VTable,
1192 $(pub $method: fn(&$name $(, $arg_ty)*) $(-> $ret)?,)*
1193 }
1194
1195 // SAFETY: the offset assertion above proves the layout
1196 // requirement; the table is `#[repr(C)]` and starts with the
1197 // parent's table.
1198 unsafe impl $crate::ObjectType for $name {
1199 type Parent = $base;
1200 type VTable = [<$name VTable>];
1201
1202 fn vtable() -> &'static Self::VTable {
1203 static VTABLE: ::std::sync::OnceLock<[<$name VTable>]> = ::std::sync::OnceLock::new();
1204 VTABLE.get_or_init(<$name as $crate::Subclassable<$name>>::build_vtable)
1205 }
1206
1207 $crate::ferro_class!(@register_class $name: $base);
1208 }
1209
1210 /// The overridable members introduced by the class.
1211 pub trait $impl_trait: $parent_impl + $crate::Upcast<$name> {
1212 /// The names of the members this implementation overrides,
1213 /// where it states them: `None` for an implementation
1214 /// written as a plain `impl`. Set by `ferro_impl_classes!`
1215 /// and `ferro_overrides!`, never by hand: the table of the
1216 /// class is built from it.
1217 #[doc(hidden)]
1218 const __OVERRIDES: ::std::option::Option<&'static [&'static str]> = ::std::option::Option::None;
1219
1220 $(
1221 $(#[$meta])*
1222 #[allow(unused_variables)]
1223 fn $method(this: &Self $(, $arg: $arg_ty)*) $(-> $ret)? {
1224 <Self as [<$impl_trait Ext>]>::[<parent_ $method>](this $(, $arg)*)
1225 }
1226 )*
1227 }
1228
1229 /// Access to the base implementations of the overridable members.
1230 pub trait [<$impl_trait Ext>]: $impl_trait {
1231 $(
1232 /// Calls the base class implementation of the member.
1233 #[inline]
1234 fn [<parent_ $method>](this: &Self $(, $arg: $arg_ty)*) $(-> $ret)? {
1235 let table = $crate::parent_vtable::<Self, [<$name VTable>]>();
1236 (table.$method)($crate::Upcast::<$name>::upcast(this) $(, $arg)*)
1237 }
1238 )*
1239 }
1240
1241 impl<T: $impl_trait> [<$impl_trait Ext>] for T {}
1242
1243 impl<T: $impl_trait> $crate::Subclassable<T> for $name
1244 where
1245 $base: $crate::Subclassable<T>,
1246 {
1247 fn build_vtable() -> [<$name VTable>] {
1248 [<$name VTable>] {
1249 base: <$base as $crate::Subclassable<T>>::build_vtable(),
1250 $(
1251 $method: {
1252 // SAFETY: this table is only attached to objects
1253 // whose most-derived class is `T`.
1254 let own: fn(&$name $(, $arg_ty)*) $(-> $ret)? =
1255 |this $(, $arg)*| T::$method(unsafe { $crate::cast_this::<$name, T>(this) } $(, $arg)*);
1256 // A member `T` states it does not override is
1257 // the default, which calls this slot of the
1258 // parent table: the slot itself is taken.
1259 let forwards: bool = const {
1260 $crate::__forwards_to_parent::<T, [<$name VTable>]>(
1261 <T as $impl_trait>::__OVERRIDES,
1262 ::std::stringify!($method),
1263 )
1264 };
1265 if forwards {
1266 $crate::parent_vtable::<T, [<$name VTable>]>().$method
1267 } else {
1268 own
1269 }
1270 },
1271 )*
1272 }
1273 }
1274 }
1275
1276 impl $name {
1277 $(
1278 $(#[$meta])*
1279 #[inline]
1280 pub fn $method(&self $(, $arg: $arg_ty)*) $(-> $ret)? {
1281 $crate::perf_count_virtual!(concat!(stringify!($name), "::", stringify!($method)));
1282 let table = $crate::FerroObject::vtable_of::<$name>(self);
1283 (table.$method)(self $(, $arg)*)
1284 }
1285 )*
1286 }
1287 }
1288 };
1289}
1290
1291/// Declares the facts about a class that untyped code (markup, untyped
1292/// bindings) needs and cannot derive: its default constructor, the
1293/// interface handles its handle converts to and its markup metadata. Every
1294/// part is optional; the parts may be written in any order.
1295///
1296/// ```ignore
1297/// ferro_class!(SolidColorBrush: Brush);
1298/// ferro_class_info!(SolidColorBrush {
1299/// new: SolidColorBrush::new,
1300/// interfaces: [Rc<dyn IBrush>],
1301/// });
1302///
1303/// ferro_class!(Panel: Control);
1304/// ferro_class_info!(Panel {
1305/// new: Panel::new,
1306/// markup: {
1307/// content: Children,
1308/// properties: [
1309/// Children: Rc<Controls> { get: Panel::children },
1310/// ],
1311/// },
1312/// });
1313/// ```
1314///
1315/// * `new` is a function without parameters that returns the handle of a new
1316/// instance. It becomes [`TypeInfo::default_constructor`].
1317/// * `interfaces` lists the handle types `I` that `Ref<Class>` converts to
1318/// with `Into<I>`, or, as `I => function`, with the given
1319/// `fn(Ref<Class>) -> I` (for an interface of another crate, for which
1320/// the conversion trait cannot be implemented). They are registered with the untyped value conversions
1321/// when the class is initialised, for the class and every class deriving
1322/// from it (a more derived class that lists the same interface takes
1323/// precedence for its own instances), and are listed by
1324/// [`TypeInfo::interfaces`].
1325/// * `markup` states what markup needs beyond the registered properties of
1326/// the class, which need no declaration: the content property, plain
1327/// (non-registered) properties and collections, constructors with
1328/// arguments, methods, static values, events and attributes. The parts are
1329/// the ones of [`ferro_markup_type!`](crate::ferro_markup_type) (without
1330/// `handles`, `this` and `base`, which follow from the class); instance
1331/// members are called with `&Ref<Class>`, so `Class::method` paths work as
1332/// callables. It becomes [`TypeInfo::markup`]: constant data that costs
1333/// nothing until it is read.
1334#[macro_export]
1335macro_rules! ferro_class_info {
1336 (@new $name:ident $constructor:expr) => {
1337 impl $name {
1338 #[doc(hidden)]
1339 pub const __CONSTRUCTOR: ::std::option::Option<fn() -> $crate::Ref<$crate::FerroObject>> =
1340 ::std::option::Option::Some(|| $crate::Ref::<$name>::upcast($constructor()));
1341 }
1342 };
1343
1344 (@cast) => {
1345 |object| ::std::convert::Into::into(object)
1346 };
1347
1348 (@cast $cast:expr) => {
1349 $cast
1350 };
1351
1352 (@interfaces $name:ident [$($interface:ty $(=> $cast:expr)?),* $(,)?]) => {
1353 impl $name {
1354 #[doc(hidden)]
1355 pub const __INTERFACES: &'static [$crate::metadata::TypeOf] =
1356 &[$(|| $crate::data::core::ValueType::of::<$interface>()),*];
1357
1358 #[doc(hidden)]
1359 pub fn __register_interfaces() {
1360 $(
1361 $crate::data::core::ValueTypes::register_interface::<$name, $interface>(
1362 $crate::ferro_class_info!(@cast $($cast)?),
1363 );
1364 )*
1365 }
1366 }
1367 };
1368
1369 (@markup $name:ident { $($body:tt)* }) => {
1370 impl $name {
1371 $crate::__ferro_markup_functions!($crate::__ferro_markup_fns!($crate::Ref<$name>, $crate::Ref<$name>; $($body)*););
1372
1373 #[doc(hidden)]
1374 pub const __MARKUP: ::std::option::Option<&'static $crate::metadata::MarkupType> = {
1375 static MARKUP: $crate::metadata::MarkupType = {
1376 #[allow(unused_mut)]
1377 let mut markup = $crate::metadata::MarkupType::new(
1378 ::std::stringify!($name),
1379 $crate::metadata::MarkupTypeKind::Class,
1380 ::std::module_path!(),
1381 );
1382 markup.handles = &[
1383 || $crate::data::core::ValueType::of::<$crate::Ref<$name>>(),
1384 || $crate::data::core::ValueType::of::<::std::option::Option<$crate::Ref<$name>>>(),
1385 ];
1386 markup.type_info = ::std::option::Option::Some(|| <$name as $crate::StaticType>::TYPE);
1387 markup.interfaces = {
1388 #[allow(unused_imports)]
1389 use $crate::ClassDefaults as _;
1390 <$name>::__INTERFACES
1391 };
1392 $crate::__ferro_compiler_metadata!(
1393 markup.this = ::std::option::Option::Some(|| $crate::data::core::ValueType::of::<$crate::Ref<$name>>());
1394 markup.value = ::std::option::Option::Some(|| $crate::data::core::ValueType::of::<$crate::Ref<$name>>());
1395 );
1396 $crate::__ferro_markup_items!(markup, $crate::Ref<$name>; $($body)*);
1397 markup
1398 };
1399 ::std::option::Option::Some(&MARKUP)
1400 };
1401 }
1402 };
1403
1404 (@parts $name:ident;) => {};
1405
1406 (@parts $name:ident; new: $constructor:expr $(, $($rest:tt)*)?) => {
1407 $crate::ferro_class_info!(@new $name $constructor);
1408 $crate::ferro_class_info!(@parts $name; $($($rest)*)?);
1409 };
1410
1411 (@parts $name:ident; interfaces: $interfaces:tt $(, $($rest:tt)*)?) => {
1412 $crate::ferro_class_info!(@interfaces $name $interfaces);
1413 $crate::ferro_class_info!(@parts $name; $($($rest)*)?);
1414 };
1415
1416 (@parts $name:ident; markup: $markup:tt $(, $($rest:tt)*)?) => {
1417 $crate::ferro_class_info!(@markup $name $markup);
1418 $crate::ferro_class_info!(@parts $name; $($($rest)*)?);
1419 };
1420
1421 ($name:ident { $($parts:tt)* }) => {
1422 $crate::ferro_class_info!(@parts $name; $($parts)*);
1423 };
1424}
1425
1426/// Declares the runtime type of a type that is not a class: a type that
1427/// owns attached properties and cannot be instantiated as an object.
1428///
1429/// ```ignore
1430/// pub struct KeyboardNavigation;
1431/// ferro_static_type!(KeyboardNavigation);
1432///
1433/// ferro_properties! {
1434/// impl KeyboardNavigation {
1435/// pub fn tab_index_property() -> AttachedProperty<i32> { .. }
1436/// }
1437/// }
1438/// ```
1439///
1440/// Unlike a hand-written `impl StaticType`, the runtime type carries the
1441/// static initialisation of the type (its `ferro_properties!` block and its
1442/// `fn static_constructor()`, as for a class), so its properties are found
1443/// by owner type and name before any accessor was called, and it knows its
1444/// module, hence its namespace.
1445#[macro_export]
1446macro_rules! ferro_static_type {
1447 ($name:ident) => {
1448 impl $crate::StaticType for $name {
1449 const TYPE: &'static $crate::TypeInfo = {
1450 fn class_init() {
1451 $crate::ferro_thread_local! {
1452 static REGISTERED: ::std::cell::Cell<bool> = const { ::std::cell::Cell::new(false) };
1453 }
1454 if !REGISTERED.replace(true) {
1455 $crate::TypeInfo::register(<$name as $crate::StaticType>::TYPE);
1456 $crate::ferro_class!(@class_members $name);
1457 }
1458 }
1459 static TYPE: $crate::TypeInfo = $crate::TypeInfo::new(stringify!($name), None)
1460 .with_module_path(::std::module_path!())
1461 .with_class_init(class_init);
1462 &TYPE
1463 };
1464 }
1465
1466 impl $name {
1467 /// The runtime type of this type.
1468 #[allow(dead_code)]
1469 pub const TYPE: &'static $crate::TypeInfo = <$name as $crate::StaticType>::TYPE;
1470 }
1471 };
1472}
1473
1474/// Implements class `Impl` traits with no overrides.
1475///
1476/// ```ignore
1477/// ferro_impl_classes!(Border: FerroObjectImpl, StyledElementImpl, VisualImpl);
1478/// ```
1479///
1480/// Unlike `impl VisualImpl for Border {}`, which means the same, this form
1481/// states that nothing is overridden, and the table of the class is built
1482/// from that: every slot of the members of these traits is the slot of the
1483/// base class, so a call reaches the implementation without passing through
1484/// a forwarding function of this class.
1485#[macro_export]
1486macro_rules! ferro_impl_classes {
1487 ($name:ty : $($trait_:path),+ $(,)?) => {
1488 $(
1489 impl $trait_ for $name {
1490 const __OVERRIDES: ::std::option::Option<&'static [&'static str]> = ::std::option::Option::Some(&[]);
1491 }
1492 )+
1493 };
1494}
1495
1496/// Implements a class `Impl` trait and states which of its members the
1497/// implementation overrides: the ones written in the block.
1498///
1499/// ```ignore
1500/// ferro_overrides! { impl FerroObjectImpl for Border {
1501/// fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
1502/// Self::parent_on_property_changed(this, change);
1503/// // ..
1504/// }
1505/// } }
1506/// ```
1507///
1508/// The implementation is the one a plain `impl FerroObjectImpl for Border`
1509/// with the same functions gives. What the macro adds is the list of their
1510/// names, from which the table of the class is built: the slot of a member
1511/// that is not in the block is the slot of the base class, so a call of it
1512/// reaches the implementation without passing through a forwarding function
1513/// of this class. The list is taken from the functions themselves and cannot
1514/// differ from them.
1515///
1516/// The block holds functions only, each with its attributes and
1517/// documentation, as the members of an `Impl` trait are.
1518#[macro_export]
1519macro_rules! ferro_overrides {
1520 (
1521 impl $trait_:ident for $name:ident {
1522 $(
1523 $(#[$meta:meta])*
1524 fn $method:ident ( $($params:tt)* ) $(-> $ret:ty)? $body:block
1525 )*
1526 }
1527 ) => {
1528 impl $trait_ for $name {
1529 const __OVERRIDES: ::std::option::Option<&'static [&'static str]> =
1530 ::std::option::Option::Some(&[$(::std::stringify!($method)),*]);
1531
1532 $(
1533 $(#[$meta])*
1534 fn $method($($params)*) $(-> $ret)? $body
1535 )*
1536 }
1537 };
1538}