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 two weak references point at the same object, alive or not.
903 #[inline]
904 pub fn ptr_eq<U: ObjectType>(&self, other: &WeakRef<U>) -> bool {
905 std::ptr::addr_eq(self.ptr.as_ptr(), other.ptr.as_ptr())
906 }
907
908 #[inline]
909 pub(crate) fn from_weak(ptr: Weak<dyn Any>) -> Self {
910 WeakRef::of(ptr)
911 }
912}
913
914impl<T: ObjectType> Clone for WeakRef<T> {
915 #[inline]
916 fn clone(&self) -> Self {
917 WeakRef::of(self.ptr.clone())
918 }
919}
920
921/// An optional object reference accepted by setters, convertible from a
922/// handle (or a borrowed handle) of any derived class and from `None`.
923pub struct Nullable<T: ObjectType>(pub Option<Ref<T>>);
924
925impl<T: ObjectType + Upcast<U>, U: ObjectType> From<Ref<T>> for Nullable<U> {
926 #[inline]
927 fn from(value: Ref<T>) -> Self {
928 Nullable(Some(value.upcast()))
929 }
930}
931
932impl<T: ObjectType + Upcast<U>, U: ObjectType> From<&Ref<T>> for Nullable<U> {
933 #[inline]
934 fn from(value: &Ref<T>) -> Self {
935 Nullable(Some(value.clone().upcast()))
936 }
937}
938
939impl<U: ObjectType> From<Option<Ref<U>>> for Nullable<U> {
940 #[inline]
941 fn from(value: Option<Ref<U>>) -> Self {
942 Nullable(value)
943 }
944}
945
946/// A non-optional object reference accepted by methods, convertible from a
947/// handle (or a borrowed handle) of any derived class.
948pub trait IntoRef<U: ObjectType> {
949 fn into_ref(self) -> Ref<U>;
950}
951
952impl<T: ObjectType + Upcast<U>, U: ObjectType> IntoRef<U> for Ref<T> {
953 #[inline]
954 fn into_ref(self) -> Ref<U> {
955 self.upcast()
956 }
957}
958
959impl<T: ObjectType + Upcast<U>, U: ObjectType> IntoRef<U> for &Ref<T> {
960 #[inline]
961 fn into_ref(self) -> Ref<U> {
962 self.clone().upcast()
963 }
964}
965
966/// Moves a constructed class value to the heap, attaches the virtual table of
967/// its class and runs the `constructed` virtual member. The class (and its
968/// base classes) are initialised first if this is the first instance on the
969/// thread; see [`ObjectType::register_class`].
970pub fn instantiate<T: ObjectType>(value: T) -> Ref<T> {
971 T::register_class();
972 let ptr: Rc<dyn Any> = Rc::new(value);
973 let result: Ref<T> = Ref { ptr, _marker: PhantomData };
974 let object: &FerroObject = (*result).upcast();
975 let table: *const T::VTable = T::vtable();
976 // SAFETY: `T::VTable` starts with the root class's table (`ObjectType`
977 // invariant), and the object's most-derived class is `T`.
978 unsafe { object.attach(Rc::downgrade(&result.ptr), table as *const ()) };
979 object.constructed();
980 result
981}
982
983/// Declares a class.
984///
985/// ```ignore
986/// #[repr(C)]
987/// pub struct Decorator { base: Control, /* fields */ }
988///
989/// // A class without new virtual members:
990/// ferro_class!(Decorator: Control);
991///
992/// // A class that introduces virtual members:
993/// ferro_class! {
994/// Layoutable: Visual, virtuals LayoutableImpl: VisualImpl {
995/// /// Measures the control.
996/// fn measure_override(this, available_size: Size) -> Size;
997/// fn arrange_override(this, final_size: Size) -> Size;
998/// }
999/// }
1000/// ```
1001///
1002/// Both forms generate `Deref<Target = Base>`, [`Upcast`] to every ancestor,
1003/// the `TYPE` runtime type and [`ObjectType`], including the static
1004/// initialisation of the class ([`ObjectType::register_class`]). What the
1005/// initialisation does is taken from the other declarations of the class,
1006/// each optional and found by the macro without being named here:
1007///
1008/// * `ferro_properties! { impl Decorator { .. } }`, the property definitions;
1009/// * `fn static_constructor()`, an inherent function declared in the same
1010/// module ([`ClassDefaults::static_constructor`]);
1011/// * `ferro_class_info!(Decorator { new: Decorator::new, interfaces: [..] })`,
1012/// the default constructor and the interface handles the class converts
1013/// to.
1014///
1015/// The second form also generates:
1016///
1017/// * `LayoutableVTable`, the table of the new members;
1018/// * `trait LayoutableImpl`, with one associated function per member taking
1019/// `this: &Self`. Every class deriving from `Layoutable` implements it
1020/// (`impl LayoutableImpl for Border {}`) and overrides what it needs
1021/// ([`ferro_impl_classes!`] and [`ferro_overrides!`] write the same
1022/// implementation and state what it overrides, which shortens the calls);
1023/// * `LayoutableImplExt::parent_<member>`, to call the base implementation
1024/// from an override;
1025/// * `Layoutable::<member>(&self, ..)`, which performs the virtual call.
1026///
1027/// The declaring class must implement every member itself; these are the
1028/// base implementations.
1029#[macro_export]
1030macro_rules! ferro_class {
1031 // What the type declares, in the order of a static initialisation:
1032 // interface casts, properties (field initialisers), static constructor.
1033 // Inherent items of the type win over the `ClassDefaults` fallbacks.
1034 (@class_members $name:ident) => {{
1035 #[allow(unused_imports)]
1036 use $crate::ClassDefaults as _;
1037 $crate::data::core::ValueTypes::register_deferred(<$name>::__register_interfaces);
1038 <$name>::__register_properties();
1039 <$name>::static_constructor();
1040 }};
1041
1042 (@register_class $name:ident : $base:ident) => {
1043 #[inline]
1044 fn register_class() {
1045 $crate::ferro_thread_local! {
1046 static REGISTERED: ::std::cell::Cell<bool> = const { ::std::cell::Cell::new(false) };
1047 }
1048 #[cold]
1049 #[inline(never)]
1050 fn init() {
1051 <$base as $crate::ObjectType>::register_class();
1052 $crate::__register_class::<$name>();
1053 $crate::ferro_class!(@class_members $name);
1054 }
1055 if !REGISTERED.get() {
1056 // Set first: the initialisation may create instances of the
1057 // class or call its property accessors.
1058 REGISTERED.set(true);
1059 init();
1060 }
1061 }
1062 };
1063
1064 (@common $name:ident : $base:ident) => {
1065 const _: () = assert!(
1066 ::std::mem::offset_of!($name, base) == 0,
1067 "class structs must be #[repr(C)] with `base` as their first field"
1068 );
1069
1070 impl ::std::ops::Deref for $name {
1071 type Target = $base;
1072 #[inline]
1073 fn deref(&self) -> &$base {
1074 &self.base
1075 }
1076 }
1077
1078 impl<T: ?Sized> $crate::Upcast<T> for $name
1079 where
1080 $base: $crate::Upcast<T>,
1081 {
1082 #[inline]
1083 fn upcast(&self) -> &T {
1084 $crate::Upcast::upcast(&self.base)
1085 }
1086 }
1087
1088 impl $crate::Upcast<$name> for $name {
1089 #[inline]
1090 fn upcast(&self) -> &$name {
1091 self
1092 }
1093 }
1094
1095 impl $crate::StaticType for $name {
1096 const TYPE: &'static $crate::TypeInfo = {
1097 static TYPE: $crate::TypeInfo = $crate::TypeInfo::new(
1098 stringify!($name),
1099 Some(<$base as $crate::StaticType>::TYPE),
1100 )
1101 .with_module_path(::std::module_path!())
1102 .with_class_init(<$name as $crate::ObjectType>::register_class)
1103 .with_constructor({
1104 #[allow(unused_imports)]
1105 use $crate::ClassDefaults as _;
1106 <$name>::__CONSTRUCTOR
1107 })
1108 .with_handles(|| {
1109 [
1110 ::std::any::TypeId::of::<$crate::Ref<$name>>(),
1111 ::std::any::TypeId::of::<::std::option::Option<$crate::Ref<$name>>>(),
1112 ]
1113 })
1114 .with_handle_registration($crate::data::core::ValueTypes::register_object::<$name>)
1115 .with_interfaces({
1116 #[allow(unused_imports)]
1117 use $crate::ClassDefaults as _;
1118 <$name>::__INTERFACES
1119 })
1120 .with_markup({
1121 #[allow(unused_imports)]
1122 use $crate::ClassDefaults as _;
1123 <$name>::__MARKUP
1124 });
1125 &TYPE
1126 };
1127 }
1128
1129 impl $name {
1130 /// The runtime type of this class.
1131 pub const TYPE: &'static $crate::TypeInfo = <$name as $crate::StaticType>::TYPE;
1132
1133 /// Returns a strong reference to this object.
1134 #[inline]
1135 pub fn to_ref(&self) -> $crate::Ref<$name> {
1136 $crate::FerroObject::ref_of(self)
1137 }
1138 }
1139 };
1140
1141 ($name:ident : $base:ident) => {
1142 $crate::ferro_class!(@common $name: $base);
1143
1144 // SAFETY: the offset assertion above proves the layout requirement;
1145 // the table type is the parent's.
1146 unsafe impl $crate::ObjectType for $name {
1147 type Parent = $base;
1148 type VTable = <$base as $crate::ObjectType>::VTable;
1149
1150 fn vtable() -> &'static Self::VTable {
1151 static VTABLE: ::std::sync::OnceLock<<$base as $crate::ObjectType>::VTable> =
1152 ::std::sync::OnceLock::new();
1153 VTABLE.get_or_init(<$name as $crate::Subclassable<$name>>::build_vtable)
1154 }
1155
1156 $crate::ferro_class!(@register_class $name: $base);
1157 }
1158
1159 impl<T> $crate::Subclassable<T> for $name
1160 where
1161 $base: $crate::Subclassable<T>,
1162 {
1163 #[inline]
1164 fn build_vtable() -> Self::VTable {
1165 <$base as $crate::Subclassable<T>>::build_vtable()
1166 }
1167 }
1168 };
1169
1170 (
1171 $name:ident : $base:ident, virtuals $impl_trait:ident : $parent_impl:path {
1172 $(
1173 $(#[$meta:meta])*
1174 fn $method:ident(this $(, $arg:ident : $arg_ty:ty)* $(,)?) $(-> $ret:ty)?;
1175 )*
1176 }
1177 ) => {
1178 $crate::ferro_class!(@common $name: $base);
1179
1180 $crate::__paste! {
1181 /// The virtual table of the class.
1182 #[derive(Clone, Copy)]
1183 #[repr(C)]
1184 pub struct [<$name VTable>] {
1185 pub base: <$base as $crate::ObjectType>::VTable,
1186 $(pub $method: fn(&$name $(, $arg_ty)*) $(-> $ret)?,)*
1187 }
1188
1189 // SAFETY: the offset assertion above proves the layout
1190 // requirement; the table is `#[repr(C)]` and starts with the
1191 // parent's table.
1192 unsafe impl $crate::ObjectType for $name {
1193 type Parent = $base;
1194 type VTable = [<$name VTable>];
1195
1196 fn vtable() -> &'static Self::VTable {
1197 static VTABLE: ::std::sync::OnceLock<[<$name VTable>]> = ::std::sync::OnceLock::new();
1198 VTABLE.get_or_init(<$name as $crate::Subclassable<$name>>::build_vtable)
1199 }
1200
1201 $crate::ferro_class!(@register_class $name: $base);
1202 }
1203
1204 /// The overridable members introduced by the class.
1205 pub trait $impl_trait: $parent_impl + $crate::Upcast<$name> {
1206 /// The names of the members this implementation overrides,
1207 /// where it states them: `None` for an implementation
1208 /// written as a plain `impl`. Set by `ferro_impl_classes!`
1209 /// and `ferro_overrides!`, never by hand: the table of the
1210 /// class is built from it.
1211 #[doc(hidden)]
1212 const __OVERRIDES: ::std::option::Option<&'static [&'static str]> = ::std::option::Option::None;
1213
1214 $(
1215 $(#[$meta])*
1216 #[allow(unused_variables)]
1217 fn $method(this: &Self $(, $arg: $arg_ty)*) $(-> $ret)? {
1218 <Self as [<$impl_trait Ext>]>::[<parent_ $method>](this $(, $arg)*)
1219 }
1220 )*
1221 }
1222
1223 /// Access to the base implementations of the overridable members.
1224 pub trait [<$impl_trait Ext>]: $impl_trait {
1225 $(
1226 /// Calls the base class implementation of the member.
1227 #[inline]
1228 fn [<parent_ $method>](this: &Self $(, $arg: $arg_ty)*) $(-> $ret)? {
1229 let table = $crate::parent_vtable::<Self, [<$name VTable>]>();
1230 (table.$method)($crate::Upcast::<$name>::upcast(this) $(, $arg)*)
1231 }
1232 )*
1233 }
1234
1235 impl<T: $impl_trait> [<$impl_trait Ext>] for T {}
1236
1237 impl<T: $impl_trait> $crate::Subclassable<T> for $name
1238 where
1239 $base: $crate::Subclassable<T>,
1240 {
1241 fn build_vtable() -> [<$name VTable>] {
1242 [<$name VTable>] {
1243 base: <$base as $crate::Subclassable<T>>::build_vtable(),
1244 $(
1245 $method: {
1246 // SAFETY: this table is only attached to objects
1247 // whose most-derived class is `T`.
1248 let own: fn(&$name $(, $arg_ty)*) $(-> $ret)? =
1249 |this $(, $arg)*| T::$method(unsafe { $crate::cast_this::<$name, T>(this) } $(, $arg)*);
1250 // A member `T` states it does not override is
1251 // the default, which calls this slot of the
1252 // parent table: the slot itself is taken.
1253 let forwards: bool = const {
1254 $crate::__forwards_to_parent::<T, [<$name VTable>]>(
1255 <T as $impl_trait>::__OVERRIDES,
1256 ::std::stringify!($method),
1257 )
1258 };
1259 if forwards {
1260 $crate::parent_vtable::<T, [<$name VTable>]>().$method
1261 } else {
1262 own
1263 }
1264 },
1265 )*
1266 }
1267 }
1268 }
1269
1270 impl $name {
1271 $(
1272 $(#[$meta])*
1273 #[inline]
1274 pub fn $method(&self $(, $arg: $arg_ty)*) $(-> $ret)? {
1275 $crate::perf_count_virtual!(concat!(stringify!($name), "::", stringify!($method)));
1276 let table = $crate::FerroObject::vtable_of::<$name>(self);
1277 (table.$method)(self $(, $arg)*)
1278 }
1279 )*
1280 }
1281 }
1282 };
1283}
1284
1285/// Declares the facts about a class that untyped code (markup, untyped
1286/// bindings) needs and cannot derive: its default constructor, the
1287/// interface handles its handle converts to and its markup metadata. Every
1288/// part is optional; the parts may be written in any order.
1289///
1290/// ```ignore
1291/// ferro_class!(SolidColorBrush: Brush);
1292/// ferro_class_info!(SolidColorBrush {
1293/// new: SolidColorBrush::new,
1294/// interfaces: [Rc<dyn IBrush>],
1295/// });
1296///
1297/// ferro_class!(Panel: Control);
1298/// ferro_class_info!(Panel {
1299/// new: Panel::new,
1300/// markup: {
1301/// content: Children,
1302/// properties: [
1303/// Children: Rc<Controls> { get: Panel::children },
1304/// ],
1305/// },
1306/// });
1307/// ```
1308///
1309/// * `new` is a function without parameters that returns the handle of a new
1310/// instance. It becomes [`TypeInfo::default_constructor`].
1311/// * `interfaces` lists the handle types `I` that `Ref<Class>` converts to
1312/// with `Into<I>`, or, as `I => function`, with the given
1313/// `fn(Ref<Class>) -> I` (for an interface of another crate, for which
1314/// the conversion trait cannot be implemented). They are registered with the untyped value conversions
1315/// when the class is initialised, for the class and every class deriving
1316/// from it (a more derived class that lists the same interface takes
1317/// precedence for its own instances), and are listed by
1318/// [`TypeInfo::interfaces`].
1319/// * `markup` states what markup needs beyond the registered properties of
1320/// the class, which need no declaration: the content property, plain
1321/// (non-registered) properties and collections, constructors with
1322/// arguments, methods, static values, events and attributes. The parts are
1323/// the ones of [`ferro_markup_type!`](crate::ferro_markup_type) (without
1324/// `handles`, `this` and `base`, which follow from the class); instance
1325/// members are called with `&Ref<Class>`, so `Class::method` paths work as
1326/// callables. It becomes [`TypeInfo::markup`]: constant data that costs
1327/// nothing until it is read.
1328#[macro_export]
1329macro_rules! ferro_class_info {
1330 (@new $name:ident $constructor:expr) => {
1331 impl $name {
1332 #[doc(hidden)]
1333 pub const __CONSTRUCTOR: ::std::option::Option<fn() -> $crate::Ref<$crate::FerroObject>> =
1334 ::std::option::Option::Some(|| $crate::Ref::<$name>::upcast($constructor()));
1335 }
1336 };
1337
1338 (@cast) => {
1339 |object| ::std::convert::Into::into(object)
1340 };
1341
1342 (@cast $cast:expr) => {
1343 $cast
1344 };
1345
1346 (@interfaces $name:ident [$($interface:ty $(=> $cast:expr)?),* $(,)?]) => {
1347 impl $name {
1348 #[doc(hidden)]
1349 pub const __INTERFACES: &'static [$crate::metadata::TypeOf] =
1350 &[$(|| $crate::data::core::ValueType::of::<$interface>()),*];
1351
1352 #[doc(hidden)]
1353 pub fn __register_interfaces() {
1354 $(
1355 $crate::data::core::ValueTypes::register_interface::<$name, $interface>(
1356 $crate::ferro_class_info!(@cast $($cast)?),
1357 );
1358 )*
1359 }
1360 }
1361 };
1362
1363 (@markup $name:ident { $($body:tt)* }) => {
1364 impl $name {
1365 $crate::__ferro_markup_functions!($crate::__ferro_markup_fns!($crate::Ref<$name>, $crate::Ref<$name>; $($body)*););
1366
1367 #[doc(hidden)]
1368 pub const __MARKUP: ::std::option::Option<&'static $crate::metadata::MarkupType> = {
1369 static MARKUP: $crate::metadata::MarkupType = {
1370 #[allow(unused_mut)]
1371 let mut markup = $crate::metadata::MarkupType::new(
1372 ::std::stringify!($name),
1373 $crate::metadata::MarkupTypeKind::Class,
1374 ::std::module_path!(),
1375 );
1376 markup.handles = &[
1377 || $crate::data::core::ValueType::of::<$crate::Ref<$name>>(),
1378 || $crate::data::core::ValueType::of::<::std::option::Option<$crate::Ref<$name>>>(),
1379 ];
1380 markup.type_info = ::std::option::Option::Some(|| <$name as $crate::StaticType>::TYPE);
1381 markup.interfaces = {
1382 #[allow(unused_imports)]
1383 use $crate::ClassDefaults as _;
1384 <$name>::__INTERFACES
1385 };
1386 $crate::__ferro_compiler_metadata!(
1387 markup.this = ::std::option::Option::Some(|| $crate::data::core::ValueType::of::<$crate::Ref<$name>>());
1388 markup.value = ::std::option::Option::Some(|| $crate::data::core::ValueType::of::<$crate::Ref<$name>>());
1389 );
1390 $crate::__ferro_markup_items!(markup, $crate::Ref<$name>; $($body)*);
1391 markup
1392 };
1393 ::std::option::Option::Some(&MARKUP)
1394 };
1395 }
1396 };
1397
1398 (@parts $name:ident;) => {};
1399
1400 (@parts $name:ident; new: $constructor:expr $(, $($rest:tt)*)?) => {
1401 $crate::ferro_class_info!(@new $name $constructor);
1402 $crate::ferro_class_info!(@parts $name; $($($rest)*)?);
1403 };
1404
1405 (@parts $name:ident; interfaces: $interfaces:tt $(, $($rest:tt)*)?) => {
1406 $crate::ferro_class_info!(@interfaces $name $interfaces);
1407 $crate::ferro_class_info!(@parts $name; $($($rest)*)?);
1408 };
1409
1410 (@parts $name:ident; markup: $markup:tt $(, $($rest:tt)*)?) => {
1411 $crate::ferro_class_info!(@markup $name $markup);
1412 $crate::ferro_class_info!(@parts $name; $($($rest)*)?);
1413 };
1414
1415 ($name:ident { $($parts:tt)* }) => {
1416 $crate::ferro_class_info!(@parts $name; $($parts)*);
1417 };
1418}
1419
1420/// Declares the runtime type of a type that is not a class: a type that
1421/// owns attached properties and cannot be instantiated as an object.
1422///
1423/// ```ignore
1424/// pub struct KeyboardNavigation;
1425/// ferro_static_type!(KeyboardNavigation);
1426///
1427/// ferro_properties! {
1428/// impl KeyboardNavigation {
1429/// pub fn tab_index_property() -> AttachedProperty<i32> { .. }
1430/// }
1431/// }
1432/// ```
1433///
1434/// Unlike a hand-written `impl StaticType`, the runtime type carries the
1435/// static initialisation of the type (its `ferro_properties!` block and its
1436/// `fn static_constructor()`, as for a class), so its properties are found
1437/// by owner type and name before any accessor was called, and it knows its
1438/// module, hence its namespace.
1439#[macro_export]
1440macro_rules! ferro_static_type {
1441 ($name:ident) => {
1442 impl $crate::StaticType for $name {
1443 const TYPE: &'static $crate::TypeInfo = {
1444 fn class_init() {
1445 $crate::ferro_thread_local! {
1446 static REGISTERED: ::std::cell::Cell<bool> = const { ::std::cell::Cell::new(false) };
1447 }
1448 if !REGISTERED.replace(true) {
1449 $crate::TypeInfo::register(<$name as $crate::StaticType>::TYPE);
1450 $crate::ferro_class!(@class_members $name);
1451 }
1452 }
1453 static TYPE: $crate::TypeInfo = $crate::TypeInfo::new(stringify!($name), None)
1454 .with_module_path(::std::module_path!())
1455 .with_class_init(class_init);
1456 &TYPE
1457 };
1458 }
1459
1460 impl $name {
1461 /// The runtime type of this type.
1462 #[allow(dead_code)]
1463 pub const TYPE: &'static $crate::TypeInfo = <$name as $crate::StaticType>::TYPE;
1464 }
1465 };
1466}
1467
1468/// Implements class `Impl` traits with no overrides.
1469///
1470/// ```ignore
1471/// ferro_impl_classes!(Border: FerroObjectImpl, StyledElementImpl, VisualImpl);
1472/// ```
1473///
1474/// Unlike `impl VisualImpl for Border {}`, which means the same, this form
1475/// states that nothing is overridden, and the table of the class is built
1476/// from that: every slot of the members of these traits is the slot of the
1477/// base class, so a call reaches the implementation without passing through
1478/// a forwarding function of this class.
1479#[macro_export]
1480macro_rules! ferro_impl_classes {
1481 ($name:ty : $($trait_:path),+ $(,)?) => {
1482 $(
1483 impl $trait_ for $name {
1484 const __OVERRIDES: ::std::option::Option<&'static [&'static str]> = ::std::option::Option::Some(&[]);
1485 }
1486 )+
1487 };
1488}
1489
1490/// Implements a class `Impl` trait and states which of its members the
1491/// implementation overrides: the ones written in the block.
1492///
1493/// ```ignore
1494/// ferro_overrides! { impl FerroObjectImpl for Border {
1495/// fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
1496/// Self::parent_on_property_changed(this, change);
1497/// // ..
1498/// }
1499/// } }
1500/// ```
1501///
1502/// The implementation is the one a plain `impl FerroObjectImpl for Border`
1503/// with the same functions gives. What the macro adds is the list of their
1504/// names, from which the table of the class is built: the slot of a member
1505/// that is not in the block is the slot of the base class, so a call of it
1506/// reaches the implementation without passing through a forwarding function
1507/// of this class. The list is taken from the functions themselves and cannot
1508/// differ from them.
1509///
1510/// The block holds functions only, each with its attributes and
1511/// documentation, as the members of an `Impl` trait are.
1512#[macro_export]
1513macro_rules! ferro_overrides {
1514 (
1515 impl $trait_:ident for $name:ident {
1516 $(
1517 $(#[$meta:meta])*
1518 fn $method:ident ( $($params:tt)* ) $(-> $ret:ty)? $body:block
1519 )*
1520 }
1521 ) => {
1522 impl $trait_ for $name {
1523 const __OVERRIDES: ::std::option::Option<&'static [&'static str]> =
1524 ::std::option::Option::Some(&[$(::std::stringify!($method)),*]);
1525
1526 $(
1527 $(#[$meta])*
1528 fn $method($($params)*) $(-> $ret)? $body
1529 )*
1530 }
1531 };
1532}