minijinja/value/
type_erase.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/// Utility macro that creates a type erased version of a trait.
///
/// This is used in the engine to create a `DynObject` for an `Object`.
/// For the exact use of this look at where the macro is invoked.
macro_rules! type_erase {
    ($v:vis trait $t_name:ident => $erased_t_name:ident {
        $(fn $f:ident(&self $(, $p:ident: $t:ty $(,)?)*) $(-> $r:ty)?;)*
        $(
            impl $impl_name:path {
                $(
                    fn $f_impl:ident(
                        &self $(, $p_impl:ident: $t_impl:ty $(,)?)*
                    ) $(-> $r_impl:ty)?;
                )*
            }
        )*
    }) => {
        #[doc = concat!("Type-erased version of [`", stringify!($t_name), "`]")]
        $v struct $erased_t_name {
            ptr: *const (),
            vtable: *const (),
        }

        const _: () = {
            struct VTable {
                $($f: fn(*const (), $($p: $t),*) $(-> $r)?,)*
                $($($f_impl: fn(*const (), $($p_impl: $t_impl),*) $(-> $r_impl)?,)*)*
                __type_id: fn() -> std::any::TypeId,
                __type_name: fn() -> &'static str,
                __incref: fn(*const ()),
                __decref: fn(*const ()),
            }

            #[inline(always)]
            fn vt(e: &$erased_t_name) -> &VTable {
                unsafe { &*(e.vtable as *const VTable) }
            }

            impl $erased_t_name {
                #[doc = concat!("Returns a new boxed, type-erased [`", stringify!($t_name), "`].")]
                $v fn new<T: $t_name + 'static>(v: std::sync::Arc<T>) -> Self {
                    let ptr = std::sync::Arc::into_raw(v) as *const T as *const ();
                    let vtable = &VTable {
                        $(
                            $f: |ptr, $($p),*| unsafe {
                                let arc = std::mem::ManuallyDrop::new(std::sync::Arc::<T>::from_raw(ptr as *const T));
                                <T as $t_name>::$f(&arc, $($p),*)
                            },
                        )*
                        $($(
                            $f_impl: |ptr, $($p_impl),*| unsafe {
                                let arc = std::mem::ManuallyDrop::new(std::sync::Arc::<T>::from_raw(ptr as *const T));
                                <T as $impl_name>::$f_impl(&*arc, $($p_impl),*)
                            },
                        )*)*
                        __type_id: || std::any::TypeId::of::<T>(),
                        __type_name: || std::any::type_name::<T>(),
                        __incref: |ptr| unsafe {
                            std::sync::Arc::<T>::increment_strong_count(ptr as *const T);
                        },
                        __decref: |ptr| unsafe {
                            std::sync::Arc::from_raw(ptr as *const T);
                        },
                    };

                    Self { ptr, vtable: vtable as *const VTable as *const () }
                }

                $(
                    #[doc = concat!(
                        "Calls [`", stringify!($t_name), "::", stringify!($f),
                        "`] of the underlying boxed value."
                    )]
                    $v fn $f(&self, $($p: $t),*) $(-> $r)? {
                        (vt(self).$f)(self.ptr, $($p),*)
                    }
                )*

                /// Returns the type name of the concrete underlying type.
                $v fn type_name(&self) -> &'static str {
                    (vt(self).__type_name)()
                }

                /// Downcast to `T` if the boxed value holds a `T`.
                ///
                /// This works like [`Any::downcast_ref`](std::any::Any#method.downcast_ref).
                $v fn downcast_ref<T: 'static>(&self) -> Option<&T> {
                    if (vt(self).__type_id)() == std::any::TypeId::of::<T>() {
                        unsafe {
                            return Some(&*(self.ptr as *const T));
                        }
                    }

                    None
                }

                /// Downcast to `T` if the boxed value holds a `T`.
                ///
                /// This is similar to [`downcast_ref`](Self::downcast_ref) but returns the [`Arc`].
                $v fn downcast<T: 'static>(&self) -> Option<Arc<T>> {
                    if (vt(self).__type_id)() == std::any::TypeId::of::<T>() {
                        unsafe {
                            std::sync::Arc::<T>::increment_strong_count(self.ptr as *const T);
                            return Some(std::sync::Arc::<T>::from_raw(self.ptr as *const T));
                        }
                    }

                    None
                }

                /// Checks if the boxed value is a `T`.
                ///
                /// This works like [`Any::is`](std::any::Any#method.is).
                $v fn is<T: 'static>(&self) -> bool {
                    self.downcast::<T>().is_some()
                }
            }

            impl Clone for $erased_t_name {
                fn clone(&self) -> Self {
                    (vt(self).__incref)(self.ptr);
                    Self {
                        ptr: self.ptr,
                        vtable: self.vtable,
                    }
                }
            }

            impl Drop for $erased_t_name {
                fn drop(&mut self) {
                    (vt(self).__decref)(self.ptr);
                }
            }

            impl<T: $t_name + 'static> From<Arc<T>> for $erased_t_name {
                fn from(value: Arc<T>) -> Self {
                    $erased_t_name::new(value)
                }
            }

            $(
                impl $impl_name for $erased_t_name {
                    $(
                        fn $f_impl(&self, $($p_impl: $t_impl),*) $(-> $r_impl)? {
                            (vt(self).$f_impl)(self.ptr, $($p_impl),*)
                        }
                    )*
                }
            )*
        };
    };
}