use crate::metadata::{MarkupType, TypeOf};
use crate::{FerroObject, FerroProperty};
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::ops::Deref;
use std::rc::{Rc, Weak};
use std::sync::{OnceLock, RwLock};
pub struct TypeInfo {
name: &'static str,
base: Option<&'static TypeInfo>,
module_path: &'static str,
class_init: Option<fn()>,
constructor: Option<fn() -> Ref<FerroObject>>,
handles: Option<fn() -> [TypeId; 2]>,
handle_registration: Option<fn()>,
interfaces: &'static [TypeOf],
markup: Option<&'static MarkupType>,
}
impl TypeInfo {
pub const fn new(name: &'static str, base: Option<&'static TypeInfo>) -> Self {
Self {
name,
base,
module_path: "",
class_init: None,
constructor: None,
handles: None,
handle_registration: None,
interfaces: &[],
markup: None,
}
}
pub const fn with_module_path(mut self, module_path: &'static str) -> Self {
self.module_path = module_path;
self
}
pub const fn with_class_init(mut self, class_init: fn()) -> Self {
self.class_init = Some(class_init);
self
}
pub const fn with_constructor(mut self, constructor: Option<fn() -> Ref<FerroObject>>) -> Self {
self.constructor = constructor;
self
}
pub const fn with_handles(mut self, handles: fn() -> [TypeId; 2]) -> Self {
self.handles = Some(handles);
self
}
pub const fn with_handle_registration(mut self, handle_registration: fn()) -> Self {
self.handle_registration = Some(handle_registration);
self
}
pub const fn with_interfaces(mut self, interfaces: &'static [TypeOf]) -> Self {
self.interfaces = interfaces;
self
}
pub const fn with_markup(mut self, markup: Option<&'static MarkupType>) -> Self {
self.markup = markup;
self
}
#[inline]
pub fn name(&self) -> &'static str {
self.name
}
#[inline]
pub fn base_type(&self) -> Option<&'static TypeInfo> {
self.base
}
#[inline]
pub fn module_path(&self) -> &'static str {
self.module_path
}
pub fn namespace(&self) -> &'static str {
let registry = read_registry();
registry.namespace_of(self.module_path)
}
pub fn namespace_of_module(module_path: &str) -> &'static str {
read_registry().namespace_of(module_path)
}
pub fn full_name(&self) -> String {
match self.namespace() {
"" => self.name.to_string(),
namespace => format!("{namespace}.{}", self.name),
}
}
#[cfg(feature = "compiler-metadata")]
pub(crate) fn has_class_init(&self) -> bool {
self.class_init.is_some()
}
#[inline]
pub fn ensure_class_init(&self) {
let mut current = Some(self);
while let Some(type_) = current {
if let Some(class_init) = type_.class_init {
class_init();
return;
}
current = type_.base;
}
}
#[inline]
pub fn default_constructor(&self) -> Option<fn() -> Ref<FerroObject>> {
self.constructor
}
pub fn create_instance(&self) -> Option<Ref<FerroObject>> {
self.constructor.map(|constructor| constructor())
}
pub fn handle(&self) -> Option<TypeId> {
self.handles.map(|handles| handles()[0])
}
pub fn nullable_handle(&self) -> Option<TypeId> {
self.handles.map(|handles| handles()[1])
}
#[inline]
pub fn interfaces(&self) -> &'static [TypeOf] {
self.interfaces
}
#[inline]
pub fn markup(&self) -> Option<&'static MarkupType> {
self.markup
}
pub fn content_property(&self) -> Option<&'static str> {
let mut current = Some(self);
while let Some(type_) = current {
if let Some(content) = type_.markup.and_then(|m| m.content_property) {
return Some(content);
}
current = type_.base;
}
None
}
pub fn properties(&'static self) -> Rc<Vec<&'static FerroProperty>> {
crate::FerroPropertyRegistry::instance().get_registered(self)
}
pub fn find_property(&'static self, name: &str) -> Option<&'static FerroProperty> {
crate::FerroPropertyRegistry::instance().find_registered(self, name)
}
pub fn register(type_: &'static TypeInfo) {
if read_registry().contains(type_) {
return;
}
write_registry().insert(type_);
if let Some(handle_registration) = type_.handle_registration {
crate::data::core::ValueTypes::register_global(handle_registration);
}
}
pub fn register_all(types: &[&'static TypeInfo]) {
{
let mut registry = write_registry();
for type_ in types {
registry.insert(type_);
}
}
for type_ in types {
if let Some(handle_registration) = type_.handle_registration {
crate::data::core::ValueTypes::register_global(handle_registration);
}
}
}
pub fn register_namespaces(namespaces: &[(&'static str, &'static str)]) {
let mut registry = write_registry();
for &(module_path, namespace) in namespaces {
match registry.namespaces.iter_mut().find(|(m, _)| *m == module_path) {
Some(entry) => entry.1 = namespace,
None => registry.namespaces.push((module_path, namespace)),
}
}
}
#[cfg(feature = "compiler-metadata")]
pub fn register_rust_paths(paths: &[(&'static TypeInfo, &'static str)]) {
let mut registry = write_registry();
for &(type_, path) in paths {
registry.rust_paths.insert(type_ as *const TypeInfo as usize, (type_, path));
}
}
#[cfg(feature = "compiler-metadata")]
pub fn rust_path(&'static self) -> Option<&'static str> {
read_registry().rust_paths.get(&(self as *const TypeInfo as usize)).map(|entry| entry.1)
}
#[cfg(feature = "compiler-metadata")]
pub fn all_rust_paths() -> Vec<(&'static TypeInfo, &'static str)> {
read_registry().rust_paths.values().copied().collect()
}
pub fn find(namespace: &str, name: &str) -> Option<&'static TypeInfo> {
let registry = read_registry();
let candidates = registry.by_name.get(name)?;
candidates.iter().copied().find(|t| registry.namespace_of(t.module_path) == namespace)
}
pub fn find_by_handle(handle: TypeId) -> Option<(&'static TypeInfo, bool)> {
read_registry().by_handle.get(&handle).copied()
}
pub fn find_by_name(name: &str) -> Option<&'static TypeInfo> {
let registry = read_registry();
match registry.by_name.get(name)?.as_slice() {
[type_] => Some(type_),
_ => None,
}
}
pub fn registered_types() -> Vec<&'static TypeInfo> {
read_registry().types.clone()
}
pub fn is_assignable_from(&self, other: &TypeInfo) -> bool {
let mut current = Some(other);
while let Some(t) = current {
if std::ptr::eq(self, t) {
return true;
}
current = t.base;
}
false
}
#[inline]
pub fn is_subclass_of(&self, other: &TypeInfo) -> bool {
other.is_assignable_from(self)
}
}
impl PartialEq for TypeInfo {
#[inline]
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
impl Eq for TypeInfo {}
impl Hash for TypeInfo {
fn hash<H: Hasher>(&self, state: &mut H) {
(self as *const TypeInfo as usize).hash(state)
}
}
impl fmt::Debug for TypeInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name)
}
}
impl fmt::Display for TypeInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name)
}
}
#[derive(Default)]
struct TypeRegistry {
types: Vec<&'static TypeInfo>,
by_name: HashMap<&'static str, Vec<&'static TypeInfo>>,
by_handle: HashMap<TypeId, (&'static TypeInfo, bool)>,
namespaces: Vec<(&'static str, &'static str)>,
#[cfg(feature = "compiler-metadata")]
rust_paths: HashMap<usize, (&'static TypeInfo, &'static str)>,
}
impl TypeRegistry {
fn contains(&self, type_: &'static TypeInfo) -> bool {
self.by_name.get(type_.name).is_some_and(|types| types.iter().any(|t| std::ptr::eq(*t, type_)))
}
fn insert(&mut self, type_: &'static TypeInfo) {
if !self.contains(type_) {
self.types.push(type_);
self.by_name.entry(type_.name).or_default().push(type_);
if let Some(handles) = type_.handles {
let [handle, nullable] = handles();
self.by_handle.insert(handle, (type_, false));
self.by_handle.insert(nullable, (type_, true));
}
}
}
fn namespace_of(&self, module_path: &str) -> &'static str {
let mut best: Option<(&'static str, &'static str)> = None;
for &(prefix, namespace) in &self.namespaces {
let matches = match module_path.strip_prefix(prefix) {
Some(rest) => rest.is_empty() || rest.starts_with("::"),
None => false,
};
if matches && best.is_none_or(|(b, _)| prefix.len() > b.len()) {
best = Some((prefix, namespace));
}
}
best.map_or("", |(_, namespace)| namespace)
}
}
fn type_registry() -> &'static RwLock<TypeRegistry> {
static REGISTRY: OnceLock<RwLock<TypeRegistry>> = OnceLock::new();
REGISTRY.get_or_init(Default::default)
}
fn read_registry() -> std::sync::RwLockReadGuard<'static, TypeRegistry> {
type_registry().read().unwrap_or_else(|e| e.into_inner())
}
fn write_registry() -> std::sync::RwLockWriteGuard<'static, TypeRegistry> {
type_registry().write().unwrap_or_else(|e| e.into_inner())
}
pub trait StaticType: 'static {
const TYPE: &'static TypeInfo;
}
pub trait ClassDefaults {
#[doc(hidden)]
const __CONSTRUCTOR: Option<fn() -> Ref<FerroObject>> = None;
#[doc(hidden)]
const __INTERFACES: &'static [TypeOf] = &[];
#[doc(hidden)]
const __MARKUP: Option<&'static MarkupType> = None;
#[doc(hidden)]
#[inline]
fn __register_interfaces() {}
#[doc(hidden)]
#[inline]
fn __register_properties() {}
#[inline]
fn static_constructor() {}
}
impl<T: ?Sized> ClassDefaults for T {}
#[doc(hidden)]
#[cold]
pub fn __register_class<T: ObjectType>() {
TypeInfo::register(T::TYPE);
crate::data::core::ValueTypes::register_deferred(crate::data::core::ValueTypes::register_object::<T>);
}
pub trait Upcast<T: ?Sized> {
fn upcast(&self) -> &T;
}
pub unsafe trait ObjectType: StaticType + Upcast<FerroObject> + Sized {
type Parent: ObjectType;
type VTable: Copy + 'static;
fn vtable() -> &'static Self::VTable;
fn register_class();
}
pub trait Subclassable<T>: ObjectType {
#[doc(hidden)]
fn build_vtable() -> Self::VTable;
}
#[doc(hidden)]
#[inline(always)]
pub unsafe fn cast_this<B, T>(base: &B) -> &T {
&*(base as *const B as *const T)
}
#[doc(hidden)]
#[inline]
pub fn parent_vtable<T: ObjectType, V>() -> &'static V {
assert!(
std::mem::size_of::<<T::Parent as ObjectType>::VTable>() >= std::mem::size_of::<V>(),
"virtual member has no base implementation"
);
let table: *const <T::Parent as ObjectType>::VTable = <T::Parent as ObjectType>::vtable();
unsafe { &*(table as *const V) }
}
#[doc(hidden)]
pub const fn __forwards_to_parent<T: ObjectType, V>(
overrides: Option<&'static [&'static str]>,
member: &str,
) -> bool {
let overrides = match overrides {
Some(overrides) => overrides,
None => return false,
};
if std::mem::size_of::<<T::Parent as ObjectType>::VTable>() < std::mem::size_of::<V>() {
return false;
}
let member = member.as_bytes();
let mut index = 0;
while index < overrides.len() {
let name = overrides[index].as_bytes();
if name.len() == member.len() {
let mut at = 0;
while at < name.len() && name[at] == member[at] {
at += 1;
}
if at == name.len() {
return false;
}
}
index += 1;
}
true
}
#[repr(transparent)]
pub struct Ref<T: ObjectType> {
ptr: Rc<dyn Any>,
_marker: PhantomData<T>,
}
impl<T: ObjectType> Ref<T> {
#[inline]
pub fn upcast<U: ObjectType>(self) -> Ref<U>
where
T: Upcast<U>,
{
Ref { ptr: self.ptr, _marker: PhantomData }
}
#[inline]
pub fn upcast_ref<U: ObjectType>(&self) -> &Ref<U>
where
T: Upcast<U>,
{
unsafe { &*(self as *const Ref<T> as *const Ref<U>) }
}
#[inline]
pub fn cast<U: ObjectType>(&self) -> Option<Ref<U>> {
if self.is::<U>() {
Some(Ref { ptr: self.ptr.clone(), _marker: PhantomData })
} else {
None
}
}
#[inline]
pub fn downcast<U: ObjectType>(self) -> Result<Ref<U>, Self> {
if self.is::<U>() {
Ok(Ref { ptr: self.ptr, _marker: PhantomData })
} else {
Err(self)
}
}
#[inline]
pub fn is<U: ObjectType>(&self) -> bool {
let object: &FerroObject = (**self).upcast();
U::TYPE.is_assignable_from(object.get_type())
}
#[inline]
pub fn downgrade(&self) -> WeakRef<T> {
WeakRef::of(Rc::downgrade(&self.ptr))
}
#[inline]
pub fn ptr_eq<U: ObjectType>(&self, other: &Ref<U>) -> bool {
std::ptr::addr_eq(Rc::as_ptr(&self.ptr), Rc::as_ptr(&other.ptr))
}
#[cfg(feature = "testing")]
#[doc(hidden)]
pub fn reference_counts(&self) -> (usize, usize, usize) {
(Rc::strong_count(&self.ptr), Rc::weak_count(&self.ptr), Rc::as_ptr(&self.ptr) as *const () as usize)
}
#[inline]
pub(crate) fn from_rc(ptr: Rc<dyn Any>) -> Self {
Ref { ptr, _marker: PhantomData }
}
}
impl<T: ObjectType> Deref for Ref<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*(Rc::as_ptr(&self.ptr) as *const T) }
}
}
impl<T: ObjectType> Clone for Ref<T> {
#[inline]
fn clone(&self) -> Self {
Ref { ptr: self.ptr.clone(), _marker: PhantomData }
}
}
impl<T: ObjectType, U: ObjectType> PartialEq<Ref<U>> for Ref<T> {
#[inline]
fn eq(&self, other: &Ref<U>) -> bool {
self.ptr_eq(other)
}
}
impl<T: ObjectType> Eq for Ref<T> {}
impl<T: ObjectType> Hash for Ref<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(Rc::as_ptr(&self.ptr) as *const () as usize).hash(state)
}
}
impl<T: ObjectType> fmt::Debug for Ref<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let object: &FerroObject = (**self).upcast();
fmt::Debug::fmt(object, f)
}
}
#[cfg(feature = "tagged-weak-references")]
pub const WEAK_REFERENCE_TAG: usize = 0x5745_414B_5245_4631_u64 as usize;
#[cfg_attr(feature = "tagged-weak-references", repr(C))]
pub struct WeakRef<T: ObjectType> {
ptr: Weak<dyn Any>,
#[cfg(feature = "tagged-weak-references")]
tag: usize,
_marker: PhantomData<T>,
}
impl<T: ObjectType> WeakRef<T> {
#[inline]
fn of(ptr: Weak<dyn Any>) -> Self {
WeakRef {
ptr,
#[cfg(feature = "tagged-weak-references")]
tag: WEAK_REFERENCE_TAG,
_marker: PhantomData,
}
}
}
impl<T: ObjectType> WeakRef<T> {
#[inline]
pub fn upgrade(&self) -> Option<Ref<T>> {
self.ptr.upgrade().map(|ptr| Ref { ptr, _marker: PhantomData })
}
#[inline]
pub fn upcast<U: ObjectType>(self) -> WeakRef<U>
where
T: Upcast<U>,
{
WeakRef::of(self.ptr)
}
#[inline]
pub fn points_to<U: ObjectType>(&self, object: &Ref<U>) -> bool {
std::ptr::addr_eq(self.ptr.as_ptr(), Rc::as_ptr(&object.ptr))
}
#[inline]
pub fn ptr_eq<U: ObjectType>(&self, other: &WeakRef<U>) -> bool {
std::ptr::addr_eq(self.ptr.as_ptr(), other.ptr.as_ptr())
}
#[inline]
pub(crate) fn from_weak(ptr: Weak<dyn Any>) -> Self {
WeakRef::of(ptr)
}
}
impl<T: ObjectType> Clone for WeakRef<T> {
#[inline]
fn clone(&self) -> Self {
WeakRef::of(self.ptr.clone())
}
}
pub struct Nullable<T: ObjectType>(pub Option<Ref<T>>);
impl<T: ObjectType + Upcast<U>, U: ObjectType> From<Ref<T>> for Nullable<U> {
#[inline]
fn from(value: Ref<T>) -> Self {
Nullable(Some(value.upcast()))
}
}
impl<T: ObjectType + Upcast<U>, U: ObjectType> From<&Ref<T>> for Nullable<U> {
#[inline]
fn from(value: &Ref<T>) -> Self {
Nullable(Some(value.clone().upcast()))
}
}
impl<U: ObjectType> From<Option<Ref<U>>> for Nullable<U> {
#[inline]
fn from(value: Option<Ref<U>>) -> Self {
Nullable(value)
}
}
pub trait IntoRef<U: ObjectType> {
fn into_ref(self) -> Ref<U>;
}
impl<T: ObjectType + Upcast<U>, U: ObjectType> IntoRef<U> for Ref<T> {
#[inline]
fn into_ref(self) -> Ref<U> {
self.upcast()
}
}
impl<T: ObjectType + Upcast<U>, U: ObjectType> IntoRef<U> for &Ref<T> {
#[inline]
fn into_ref(self) -> Ref<U> {
self.clone().upcast()
}
}
pub fn instantiate<T: ObjectType>(value: T) -> Ref<T> {
T::register_class();
let ptr: Rc<dyn Any> = Rc::new(value);
let result: Ref<T> = Ref { ptr, _marker: PhantomData };
let object: &FerroObject = (*result).upcast();
let table: *const T::VTable = T::vtable();
unsafe { object.attach(Rc::downgrade(&result.ptr), table as *const ()) };
object.constructed();
result
}
#[macro_export]
macro_rules! ferro_class {
(@class_members $name:ident) => {{
#[allow(unused_imports)]
use $crate::ClassDefaults as _;
$crate::data::core::ValueTypes::register_deferred(<$name>::__register_interfaces);
<$name>::__register_properties();
<$name>::static_constructor();
}};
(@register_class $name:ident : $base:ident) => {
#[inline]
fn register_class() {
$crate::ferro_thread_local! {
static REGISTERED: ::std::cell::Cell<bool> = const { ::std::cell::Cell::new(false) };
}
#[cold]
#[inline(never)]
fn init() {
<$base as $crate::ObjectType>::register_class();
$crate::__register_class::<$name>();
$crate::ferro_class!(@class_members $name);
}
if !REGISTERED.get() {
REGISTERED.set(true);
init();
}
}
};
(@common $name:ident : $base:ident) => {
const _: () = assert!(
::std::mem::offset_of!($name, base) == 0,
"class structs must be #[repr(C)] with `base` as their first field"
);
impl ::std::ops::Deref for $name {
type Target = $base;
#[inline]
fn deref(&self) -> &$base {
&self.base
}
}
impl<T: ?Sized> $crate::Upcast<T> for $name
where
$base: $crate::Upcast<T>,
{
#[inline]
fn upcast(&self) -> &T {
$crate::Upcast::upcast(&self.base)
}
}
impl $crate::Upcast<$name> for $name {
#[inline]
fn upcast(&self) -> &$name {
self
}
}
impl $crate::StaticType for $name {
const TYPE: &'static $crate::TypeInfo = {
static TYPE: $crate::TypeInfo = $crate::TypeInfo::new(
stringify!($name),
Some(<$base as $crate::StaticType>::TYPE),
)
.with_module_path(::std::module_path!())
.with_class_init(<$name as $crate::ObjectType>::register_class)
.with_constructor({
#[allow(unused_imports)]
use $crate::ClassDefaults as _;
<$name>::__CONSTRUCTOR
})
.with_handles(|| {
[
::std::any::TypeId::of::<$crate::Ref<$name>>(),
::std::any::TypeId::of::<::std::option::Option<$crate::Ref<$name>>>(),
]
})
.with_handle_registration($crate::data::core::ValueTypes::register_object::<$name>)
.with_interfaces({
#[allow(unused_imports)]
use $crate::ClassDefaults as _;
<$name>::__INTERFACES
})
.with_markup({
#[allow(unused_imports)]
use $crate::ClassDefaults as _;
<$name>::__MARKUP
});
&TYPE
};
}
impl $name {
pub const TYPE: &'static $crate::TypeInfo = <$name as $crate::StaticType>::TYPE;
#[inline]
pub fn to_ref(&self) -> $crate::Ref<$name> {
$crate::FerroObject::ref_of(self)
}
}
};
($name:ident : $base:ident) => {
$crate::ferro_class!(@common $name: $base);
unsafe impl $crate::ObjectType for $name {
type Parent = $base;
type VTable = <$base as $crate::ObjectType>::VTable;
fn vtable() -> &'static Self::VTable {
static VTABLE: ::std::sync::OnceLock<<$base as $crate::ObjectType>::VTable> =
::std::sync::OnceLock::new();
VTABLE.get_or_init(<$name as $crate::Subclassable<$name>>::build_vtable)
}
$crate::ferro_class!(@register_class $name: $base);
}
impl<T> $crate::Subclassable<T> for $name
where
$base: $crate::Subclassable<T>,
{
#[inline]
fn build_vtable() -> Self::VTable {
<$base as $crate::Subclassable<T>>::build_vtable()
}
}
};
(
$name:ident : $base:ident, virtuals $impl_trait:ident : $parent_impl:path {
$(
$(#[$meta:meta])*
fn $method:ident(this $(, $arg:ident : $arg_ty:ty)* $(,)?) $(-> $ret:ty)?;
)*
}
) => {
$crate::ferro_class!(@common $name: $base);
$crate::__paste! {
#[derive(Clone, Copy)]
#[repr(C)]
pub struct [<$name VTable>] {
pub base: <$base as $crate::ObjectType>::VTable,
$(pub $method: fn(&$name $(, $arg_ty)*) $(-> $ret)?,)*
}
unsafe impl $crate::ObjectType for $name {
type Parent = $base;
type VTable = [<$name VTable>];
fn vtable() -> &'static Self::VTable {
static VTABLE: ::std::sync::OnceLock<[<$name VTable>]> = ::std::sync::OnceLock::new();
VTABLE.get_or_init(<$name as $crate::Subclassable<$name>>::build_vtable)
}
$crate::ferro_class!(@register_class $name: $base);
}
pub trait $impl_trait: $parent_impl + $crate::Upcast<$name> {
#[doc(hidden)]
const __OVERRIDES: ::std::option::Option<&'static [&'static str]> = ::std::option::Option::None;
$(
$(#[$meta])*
#[allow(unused_variables)]
fn $method(this: &Self $(, $arg: $arg_ty)*) $(-> $ret)? {
<Self as [<$impl_trait Ext>]>::[<parent_ $method>](this $(, $arg)*)
}
)*
}
pub trait [<$impl_trait Ext>]: $impl_trait {
$(
#[inline]
fn [<parent_ $method>](this: &Self $(, $arg: $arg_ty)*) $(-> $ret)? {
let table = $crate::parent_vtable::<Self, [<$name VTable>]>();
(table.$method)($crate::Upcast::<$name>::upcast(this) $(, $arg)*)
}
)*
}
impl<T: $impl_trait> [<$impl_trait Ext>] for T {}
impl<T: $impl_trait> $crate::Subclassable<T> for $name
where
$base: $crate::Subclassable<T>,
{
fn build_vtable() -> [<$name VTable>] {
[<$name VTable>] {
base: <$base as $crate::Subclassable<T>>::build_vtable(),
$(
$method: {
let own: fn(&$name $(, $arg_ty)*) $(-> $ret)? =
|this $(, $arg)*| T::$method(unsafe { $crate::cast_this::<$name, T>(this) } $(, $arg)*);
let forwards: bool = const {
$crate::__forwards_to_parent::<T, [<$name VTable>]>(
<T as $impl_trait>::__OVERRIDES,
::std::stringify!($method),
)
};
if forwards {
$crate::parent_vtable::<T, [<$name VTable>]>().$method
} else {
own
}
},
)*
}
}
}
impl $name {
$(
$(#[$meta])*
#[inline]
pub fn $method(&self $(, $arg: $arg_ty)*) $(-> $ret)? {
$crate::perf_count_virtual!(concat!(stringify!($name), "::", stringify!($method)));
let table = $crate::FerroObject::vtable_of::<$name>(self);
(table.$method)(self $(, $arg)*)
}
)*
}
}
};
}
#[macro_export]
macro_rules! ferro_class_info {
(@new $name:ident $constructor:expr) => {
impl $name {
#[doc(hidden)]
pub const __CONSTRUCTOR: ::std::option::Option<fn() -> $crate::Ref<$crate::FerroObject>> =
::std::option::Option::Some(|| $crate::Ref::<$name>::upcast($constructor()));
}
};
(@cast) => {
|object| ::std::convert::Into::into(object)
};
(@cast $cast:expr) => {
$cast
};
(@interfaces $name:ident [$($interface:ty $(=> $cast:expr)?),* $(,)?]) => {
impl $name {
#[doc(hidden)]
pub const __INTERFACES: &'static [$crate::metadata::TypeOf] =
&[$(|| $crate::data::core::ValueType::of::<$interface>()),*];
#[doc(hidden)]
pub fn __register_interfaces() {
$(
$crate::data::core::ValueTypes::register_interface::<$name, $interface>(
$crate::ferro_class_info!(@cast $($cast)?),
);
)*
}
}
};
(@markup $name:ident { $($body:tt)* }) => {
impl $name {
$crate::__ferro_markup_functions!($crate::__ferro_markup_fns!($crate::Ref<$name>, $crate::Ref<$name>; $($body)*););
#[doc(hidden)]
pub const __MARKUP: ::std::option::Option<&'static $crate::metadata::MarkupType> = {
static MARKUP: $crate::metadata::MarkupType = {
#[allow(unused_mut)]
let mut markup = $crate::metadata::MarkupType::new(
::std::stringify!($name),
$crate::metadata::MarkupTypeKind::Class,
::std::module_path!(),
);
markup.handles = &[
|| $crate::data::core::ValueType::of::<$crate::Ref<$name>>(),
|| $crate::data::core::ValueType::of::<::std::option::Option<$crate::Ref<$name>>>(),
];
markup.type_info = ::std::option::Option::Some(|| <$name as $crate::StaticType>::TYPE);
markup.interfaces = {
#[allow(unused_imports)]
use $crate::ClassDefaults as _;
<$name>::__INTERFACES
};
$crate::__ferro_compiler_metadata!(
markup.this = ::std::option::Option::Some(|| $crate::data::core::ValueType::of::<$crate::Ref<$name>>());
markup.value = ::std::option::Option::Some(|| $crate::data::core::ValueType::of::<$crate::Ref<$name>>());
);
$crate::__ferro_markup_items!(markup, $crate::Ref<$name>; $($body)*);
markup
};
::std::option::Option::Some(&MARKUP)
};
}
};
(@parts $name:ident;) => {};
(@parts $name:ident; new: $constructor:expr $(, $($rest:tt)*)?) => {
$crate::ferro_class_info!(@new $name $constructor);
$crate::ferro_class_info!(@parts $name; $($($rest)*)?);
};
(@parts $name:ident; interfaces: $interfaces:tt $(, $($rest:tt)*)?) => {
$crate::ferro_class_info!(@interfaces $name $interfaces);
$crate::ferro_class_info!(@parts $name; $($($rest)*)?);
};
(@parts $name:ident; markup: $markup:tt $(, $($rest:tt)*)?) => {
$crate::ferro_class_info!(@markup $name $markup);
$crate::ferro_class_info!(@parts $name; $($($rest)*)?);
};
($name:ident { $($parts:tt)* }) => {
$crate::ferro_class_info!(@parts $name; $($parts)*);
};
}
#[macro_export]
macro_rules! ferro_static_type {
($name:ident) => {
impl $crate::StaticType for $name {
const TYPE: &'static $crate::TypeInfo = {
fn class_init() {
$crate::ferro_thread_local! {
static REGISTERED: ::std::cell::Cell<bool> = const { ::std::cell::Cell::new(false) };
}
if !REGISTERED.replace(true) {
$crate::TypeInfo::register(<$name as $crate::StaticType>::TYPE);
$crate::ferro_class!(@class_members $name);
}
}
static TYPE: $crate::TypeInfo = $crate::TypeInfo::new(stringify!($name), None)
.with_module_path(::std::module_path!())
.with_class_init(class_init);
&TYPE
};
}
impl $name {
#[allow(dead_code)]
pub const TYPE: &'static $crate::TypeInfo = <$name as $crate::StaticType>::TYPE;
}
};
}
#[macro_export]
macro_rules! ferro_impl_classes {
($name:ty : $($trait_:path),+ $(,)?) => {
$(
impl $trait_ for $name {
const __OVERRIDES: ::std::option::Option<&'static [&'static str]> = ::std::option::Option::Some(&[]);
}
)+
};
}
#[macro_export]
macro_rules! ferro_overrides {
(
impl $trait_:ident for $name:ident {
$(
$(#[$meta:meta])*
fn $method:ident ( $($params:tt)* ) $(-> $ret:ty)? $body:block
)*
}
) => {
impl $trait_ for $name {
const __OVERRIDES: ::std::option::Option<&'static [&'static str]> =
::std::option::Option::Some(&[$(::std::stringify!($method)),*]);
$(
$(#[$meta])*
fn $method($($params)*) $(-> $ret)? $body
)*
}
};
}