Skip to main content

tabulon/
registry.rs

1// Re-exported metadata for functions collected via #[function]
2#[derive(Clone, Copy)]
3pub struct FnMeta {
4    pub name: &'static str,
5    pub arity: u8,
6    pub addr: *const u8,
7    pub mod_path: &'static str,
8    pub uses_ctx: bool,
9    pub ctx_type_id_fn: Option<fn() -> ::core::any::TypeId>,
10}
11
12// Safe because addr points to a 'static extern "C" function shim generated by the macro
13unsafe impl Sync for FnMeta {}
14
15inventory::collect!(FnMeta);
16
17/// A trait to extract the context type from an engine instance.
18pub trait HasCtx {
19    type Ctx;
20}
21
22impl<Ctx> HasCtx for crate::engine::Tabula<Ctx> {
23    type Ctx = Ctx;
24}
25
26#[macro_export]
27macro_rules! register_functions {
28    ($eng:expr, $($name:ident),+ $(,)?) => {{
29        // Determine the engine's context TypeId at compile site
30        #[allow(dead_code)]
31        fn __tabulon_engine_ctx_type_id<E: $crate::HasCtx>(_: &E) -> ::core::any::TypeId
32        where
33            <E as $crate::HasCtx>::Ctx: 'static,
34        {
35            ::core::any::TypeId::of::<<E as $crate::HasCtx>::Ctx>()
36        }
37        let __eng_tid = __tabulon_engine_ctx_type_id(&$eng);
38
39        let mut res: Result<(), $crate::JitError> = Ok(());
40        let wanted: &[&str] = &[ $( stringify!($name) ),+ ];
41        for meta in $crate::inventory::iter::<$crate::FnMeta> {
42            if wanted.contains(&meta.name) {
43                // Runtime type-check to prevent mismatched context registration
44                if meta.uses_ctx {
45                    if let Some(ctx_tid_fn) = meta.ctx_type_id_fn {
46                        if (ctx_tid_fn)() != __eng_tid {
47                            res = Err($crate::JitError::Internal(format!(
48                                "context type mismatch when registering {}: engine Ctx does not match function's Ctx",
49                                meta.name
50                            )));
51                            break;
52                        }
53                    }
54                }
55                res = res.and_then(|_| unsafe {
56                    match meta.arity {
57                        0 => $eng.register_nullary(meta.name, std::mem::transmute::<*const u8, $crate::Fn0>(meta.addr), meta.uses_ctx),
58                        1 => $eng.register_unary(meta.name, std::mem::transmute::<*const u8, $crate::Fn1>(meta.addr), meta.uses_ctx),
59                        2 => $eng.register_binary(meta.name, std::mem::transmute::<*const u8, $crate::Fn2>(meta.addr), meta.uses_ctx),
60                        3 => $eng.register_ternary(meta.name, std::mem::transmute::<*const u8, $crate::Fn3>(meta.addr), meta.uses_ctx),
61                        _ => Err($crate::JitError::Internal(format!("unsupported arity {} for {}", meta.arity, meta.name))),
62                    }
63                });
64            }
65        }
66        res
67    }};
68}
69
70#[macro_export]
71macro_rules! register_functions_typed {
72    ($eng:expr, $($marker:path),+ $(,)?) => {{
73        let mut res: Result<(), $crate::JitError> = Ok(());
74        $(
75            res = res.and_then(|_| $eng.register_typed::<$marker>());
76        )+
77        res
78    }};
79}
80
81/// Type-equality helper for compile-time context matching (EngineCtx == FunctionCtx).
82pub trait SameAs<T> {}
83impl<T> SameAs<T> for T {}
84
85/// Function metadata bound to an engine context type. Implemented by #[function]-generated markers.
86pub trait FunctionForEngineCtx<EngineCtx> {
87    const NAME: &'static str;
88    const ARITY: u8;
89    const USES_CTX: bool;
90    fn addr() -> *const u8;
91}
92
93/// Resolver metadata bound to an engine context type. Implemented by #[resolver]-generated markers.
94pub trait ResolverForEngineCtx<EngineCtx> {
95    /// Symbol name to register with the JIT for this resolver.
96    const NAME: &'static str;
97    /// Address of the generated extern "C" shim: fn(ctx: *mut c_void, idx: u32) -> f64
98    fn addr() -> *const u8;
99}
100
101#[macro_export]
102macro_rules! register_resolver_typed {
103    ($eng:expr, $marker:path) => {{
104        $eng.set_var_getter_typed::<$marker>()
105    }};
106}