Skip to main content

intuicio_core/
function.rs

1#![allow(unpredictable_function_pointer_comparisons)]
2
3//! Callable units, and how to describe and find them.
4//!
5//! Every function, whether it came from Rust or from a script, is a
6//! [`Function`]: a [`FunctionSignature`] describing it and a
7//! [`FunctionBody`] doing the work. The body always has the same shape,
8//! `fn(&mut Context, &Registry)`, which is why the caller cannot tell the two
9//! kinds apart.
10//!
11//! # Argument order
12//!
13//! A body pops its arguments in declaration order, first to last, and pushes
14//! its results in reverse. A caller therefore has to push arguments in reverse
15//! order. [`Function::call`] does that for you, [`Function::invoke`] does not.
16//! Prefer `call` unless you already manage the stack yourself.
17use crate::{
18    Filter, Visibility,
19    context::Context,
20    meta::Meta,
21    registry::Registry,
22    types::{Type, TypeHandle, TypeQuery},
23};
24use intuicio_data::data_stack::DataStackPack;
25use rustc_hash::FxHasher;
26use std::{
27    borrow::Cow,
28    hash::{Hash, Hasher},
29    sync::Arc,
30};
31
32/// Shared function, as a registry holds it.
33pub type FunctionHandle = Arc<Function>;
34/// Predicate over a function's metadata, used inside queries.
35pub type FunctionMetaQuery = fn(&Meta) -> bool;
36
37/// The code a function runs.
38///
39/// Both variants take the context to move data through and the registry to
40/// look up anything else they need to call.
41pub enum FunctionBody {
42    /// A plain function pointer.
43    Pointer(fn(&mut Context, &Registry)),
44    /// A closure, for bodies that capture state such as a compiled script.
45    #[allow(clippy::type_complexity)]
46    Closure(Arc<dyn Fn(&mut Context, &Registry) + Send + Sync>),
47}
48
49impl FunctionBody {
50    /// Wraps a function pointer.
51    pub fn pointer(pointer: fn(&mut Context, &Registry)) -> Self {
52        Self::Pointer(pointer)
53    }
54
55    /// Wraps a closure.
56    pub fn closure<T>(closure: T) -> Self
57    where
58        T: Fn(&mut Context, &Registry) + Send + Sync + 'static,
59    {
60        Self::Closure(Arc::new(closure))
61    }
62
63    /// Runs the body. Prefer [`Function::invoke`], which also scopes registers.
64    pub fn invoke(&self, context: &mut Context, registry: &Registry) {
65        match self {
66            Self::Pointer(pointer) => pointer(context, registry),
67            Self::Closure(closure) => closure(context, registry),
68        }
69    }
70}
71
72impl std::fmt::Debug for FunctionBody {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        match self {
75            Self::Pointer(_) => write!(f, "<Pointer>"),
76            Self::Closure(_) => write!(f, "<Closure>"),
77        }
78    }
79}
80
81/// One input or output of a function, with its name and type.
82#[derive(Clone, PartialEq)]
83pub struct FunctionParameter {
84    /// Metadata attached to this parameter.
85    pub meta: Option<Meta>,
86    /// Parameter name.
87    pub name: String,
88    /// Type of the value this parameter carries.
89    pub type_handle: TypeHandle,
90}
91
92impl FunctionParameter {
93    /// Builds a parameter.
94    pub fn new(name: impl ToString, type_handle: TypeHandle) -> Self {
95        Self {
96            meta: None,
97            name: name.to_string(),
98            type_handle,
99        }
100    }
101
102    /// Attaches metadata, builder style.
103    pub fn with_meta(mut self, meta: Meta) -> Self {
104        self.meta = Some(meta);
105        self
106    }
107}
108
109impl std::fmt::Debug for FunctionParameter {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        f.debug_struct("FunctionParameter")
112            .field("meta", &self.meta)
113            .field("name", &self.name)
114            .field("type_handle", &self.type_handle.name())
115            .finish()
116    }
117}
118
119/// Everything about a function except its body.
120///
121/// The signature is also the identity of a function: a registry refuses to
122/// hold two functions whose signatures are equal.
123///
124/// `type_handle` is set when the function belongs to a type, which is how
125/// methods are modelled.
126#[derive(Clone, PartialEq)]
127pub struct FunctionSignature {
128    /// Metadata attached to this function.
129    pub meta: Option<Meta>,
130    /// Name the function is registered under.
131    pub name: String,
132    /// Module the function belongs to.
133    pub module_name: Option<String>,
134    /// Type the function is a method of, if any.
135    pub type_handle: Option<TypeHandle>,
136    /// How widely the function is visible.
137    pub visibility: Visibility,
138    /// Arguments, in declaration order.
139    pub inputs: Vec<FunctionParameter>,
140    /// Results, in declaration order.
141    pub outputs: Vec<FunctionParameter>,
142}
143
144impl FunctionSignature {
145    /// Builds a signature with just a name.
146    pub fn new(name: impl ToString) -> Self {
147        Self {
148            meta: None,
149            name: name.to_string(),
150            module_name: None,
151            type_handle: None,
152            visibility: Visibility::default(),
153            inputs: vec![],
154            outputs: vec![],
155        }
156    }
157
158    /// Attaches metadata, builder style.
159    pub fn with_meta(mut self, meta: Meta) -> Self {
160        self.meta = Some(meta);
161        self
162    }
163
164    /// Sets the owning module, builder style.
165    pub fn with_module_name(mut self, name: impl ToString) -> Self {
166        self.module_name = Some(name.to_string());
167        self
168    }
169
170    /// Makes this a method of the given type, builder style.
171    pub fn with_type_handle(mut self, handle: TypeHandle) -> Self {
172        self.type_handle = Some(handle);
173        self
174    }
175
176    /// Sets visibility, builder style.
177    pub fn with_visibility(mut self, visibility: Visibility) -> Self {
178        self.visibility = visibility;
179        self
180    }
181
182    /// Appends an input, builder style.
183    pub fn with_input(mut self, parameter: FunctionParameter) -> Self {
184        self.inputs.push(parameter);
185        self
186    }
187
188    /// Appends an output, builder style.
189    pub fn with_output(mut self, parameter: FunctionParameter) -> Self {
190        self.outputs.push(parameter);
191        self
192    }
193}
194
195impl std::fmt::Debug for FunctionSignature {
196    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197        f.debug_struct("FunctionSignature")
198            .field("meta", &self.meta)
199            .field("name", &self.name)
200            .field("module_name", &self.module_name)
201            .field(
202                "type_handle",
203                &match self.type_handle.as_ref() {
204                    Some(type_handle) => type_handle.name().to_owned(),
205                    None => "!".to_owned(),
206                },
207            )
208            .field("visibility", &self.visibility)
209            .field("inputs", &self.inputs)
210            .field("outputs", &self.outputs)
211            .finish()
212    }
213}
214
215impl std::fmt::Display for FunctionSignature {
216    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217        if let Some(meta) = self.meta.as_ref() {
218            write!(f, "#{meta} ")?;
219        }
220        if let Some(module_name) = self.module_name.as_ref() {
221            write!(f, "mod {module_name} ")?;
222        }
223        if let Some(type_handle) = self.type_handle.as_ref() {
224            match &**type_handle {
225                Type::Struct(value) => {
226                    write!(f, "struct {} ", value.type_name())?;
227                }
228                Type::Enum(value) => {
229                    write!(f, "enum {} ", value.type_name())?;
230                }
231            }
232        }
233        write!(f, "fn {}(", self.name)?;
234        for (index, parameter) in self.inputs.iter().enumerate() {
235            if index > 0 {
236                write!(f, ", ")?;
237            }
238            write!(
239                f,
240                "{}: {}",
241                parameter.name,
242                parameter.type_handle.type_name()
243            )?;
244        }
245        write!(f, ") -> (")?;
246        for (index, parameter) in self.outputs.iter().enumerate() {
247            if index > 0 {
248                write!(f, ", ")?;
249            }
250            write!(
251                f,
252                "{}: {}",
253                parameter.name,
254                parameter.type_handle.type_name()
255            )?;
256        }
257        write!(f, ")")
258    }
259}
260
261/// A callable unit: a signature plus a body.
262///
263/// See the [module docs](self).
264#[derive(Debug)]
265pub struct Function {
266    signature: FunctionSignature,
267    body: FunctionBody,
268}
269
270impl Function {
271    /// Pairs a signature with a body.
272    pub fn new(signature: FunctionSignature, body: FunctionBody) -> Self {
273        Self { signature, body }
274    }
275
276    /// Returns the signature.
277    pub fn signature(&self) -> &FunctionSignature {
278        &self.signature
279    }
280
281    /// Runs the function on data already on the stack.
282    ///
283    /// Arguments must be pushed in reverse order beforehand, and results are
284    /// left on the stack. Registers are scoped around the body, so the callee
285    /// cannot reach the caller's.
286    pub fn invoke(&self, context: &mut Context, registry: &Registry) {
287        context.store_registers();
288        self.body.invoke(context, registry);
289        context.restore_registers();
290    }
291
292    /// Runs the function with Rust values, taking care of stack order.
293    ///
294    /// With `verify` set, argument and result types are checked against the
295    /// signature first.
296    ///
297    /// # Panics
298    ///
299    /// Panics when `verify` is set and the types do not match, or when the
300    /// results on the stack are not of type `O`.
301    pub fn call<O: DataStackPack, I: DataStackPack>(
302        &self,
303        context: &mut Context,
304        registry: &Registry,
305        inputs: I,
306        verify: bool,
307    ) -> O {
308        if verify {
309            self.verify_inputs_outputs::<O, I>();
310        }
311        inputs.stack_push_reversed(context.stack());
312        self.invoke(context, registry);
313        O::stack_pop(context.stack())
314    }
315
316    /// Checks that `I` and `O` match the signature.
317    ///
318    /// # Panics
319    ///
320    /// Panics with a message naming the offending parameter when they do not.
321    pub fn verify_inputs_outputs<O: DataStackPack, I: DataStackPack>(&self) {
322        let input_types = I::pack_types();
323        if input_types.len() != self.signature.inputs.len() {
324            panic!("Function: {} got wrong inputs number!", self.signature.name);
325        }
326        let output_types = O::pack_types();
327        if output_types.len() != self.signature.outputs.len() {
328            panic!(
329                "Function: {} got wrong outputs number!",
330                self.signature.name
331            );
332        }
333        for (parameter, type_hash) in self.signature.inputs.iter().zip(input_types) {
334            if parameter.type_handle.type_hash() != type_hash {
335                panic!(
336                    "Function: {} input parameter: {} got wrong value type!",
337                    self.signature.name, parameter.name
338                );
339            }
340        }
341        for (parameter, type_hash) in self.signature.outputs.iter().zip(output_types) {
342            if parameter.type_handle.type_hash() != type_hash {
343                panic!(
344                    "Function: {} output parameter: {} got wrong value type!",
345                    self.signature.name, parameter.name
346                );
347            }
348        }
349    }
350
351    /// Wraps this function in a shared handle.
352    pub fn into_handle(self) -> FunctionHandle {
353        self.into()
354    }
355}
356
357/// Search filter for one function parameter.
358///
359/// Every field is optional. An empty filter matches anything.
360#[derive(Debug, Default, Clone, PartialEq, Hash)]
361pub struct FunctionQueryParameter<'a> {
362    /// Required parameter name.
363    pub name: Option<Cow<'a, str>>,
364    /// Filter on the parameter type.
365    pub type_query: Option<TypeQuery<'a>>,
366    /// Predicate the parameter metadata must satisfy.
367    pub meta: Option<FunctionMetaQuery>,
368}
369
370impl FunctionQueryParameter<'_> {
371    /// Returns `true` when `parameter` satisfies every set field.
372    pub fn is_valid(&self, parameter: &FunctionParameter) -> bool {
373        self.name
374            .as_ref()
375            .map(|name| name.as_ref() == parameter.name)
376            .unwrap_or(true)
377            && self
378                .type_query
379                .as_ref()
380                .map(|query| query.is_valid(&parameter.type_handle))
381                .unwrap_or(true)
382            && self
383                .meta
384                .as_ref()
385                .map(|query| parameter.meta.as_ref().map(query).unwrap_or(false))
386                .unwrap_or(true)
387    }
388
389    /// Copies borrowed names into owned ones, so the filter can outlive them.
390    pub fn to_static(&self) -> FunctionQueryParameter<'static> {
391        FunctionQueryParameter {
392            name: self
393                .name
394                .as_ref()
395                .map(|name| name.as_ref().to_owned().into()),
396            type_query: self.type_query.as_ref().map(|query| query.to_static()),
397            meta: self.meta,
398        }
399    }
400}
401
402/// Search filter for a function's parameter list.
403///
404/// Three settings, because "the first two are ints" and "it takes exactly two
405/// ints" are different questions. A bare list can only ask one of them.
406///
407/// [`Self::Prefix`] rejects a list **longer** than the function's. A filter
408/// with no parameter to match cannot be satisfied, so it fails instead of
409/// being ignored.
410///
411/// ```
412/// # use intuicio_core::function::Parameters;
413/// // Say nothing about the parameters at all.
414/// let any = Parameters::default();
415/// assert!(matches!(any, Parameters::Any));
416/// ```
417#[derive(Debug, Default, Clone, PartialEq, Hash)]
418pub enum Parameters<'a> {
419    /// Matches any parameter list.
420    #[default]
421    Any,
422    /// Matches from the front. The function may take more, but not fewer.
423    Prefix(Cow<'a, [FunctionQueryParameter<'a>]>),
424    /// Matches one for one. The function takes exactly these.
425    Exact(Cow<'a, [FunctionQueryParameter<'a>]>),
426}
427
428/// A plain list is matched from the front. The function may take more
429/// parameters.
430impl<'a> From<Vec<FunctionQueryParameter<'a>>> for Parameters<'a> {
431    fn from(value: Vec<FunctionQueryParameter<'a>>) -> Self {
432        Self::Prefix(value.into())
433    }
434}
435
436impl<'a> From<Cow<'a, [FunctionQueryParameter<'a>]>> for Parameters<'a> {
437    fn from(value: Cow<'a, [FunctionQueryParameter<'a>]>) -> Self {
438        Self::Prefix(value)
439    }
440}
441
442impl<'a> From<&'a [FunctionQueryParameter<'a>]> for Parameters<'a> {
443    fn from(value: &'a [FunctionQueryParameter<'a>]) -> Self {
444        Self::Prefix(value.into())
445    }
446}
447
448impl<'a> Parameters<'a> {
449    /// Returns `true` when `parameters` satisfies this filter.
450    pub fn is_valid(&self, parameters: &[FunctionParameter]) -> bool {
451        let (queries, exact) = match self {
452            Self::Any => return true,
453            Self::Prefix(queries) => (queries, false),
454            Self::Exact(queries) => (queries, true),
455        };
456        let fits = if exact {
457            queries.len() == parameters.len()
458        } else {
459            queries.len() <= parameters.len()
460        };
461        fits && queries
462            .iter()
463            .zip(parameters.iter())
464            .all(|(query, parameter)| query.is_valid(parameter))
465    }
466
467    /// The filters themselves, empty for [`Self::Any`].
468    pub fn queries(&self) -> &[FunctionQueryParameter<'a>] {
469        match self {
470            Self::Any => &[],
471            Self::Prefix(queries) | Self::Exact(queries) => queries,
472        }
473    }
474
475    /// Copies borrowed names into owned ones, so the filter can outlive them.
476    pub fn to_static(&self) -> Parameters<'static> {
477        let queries = |queries: &Cow<'_, [FunctionQueryParameter<'_>]>| {
478            queries
479                .iter()
480                .map(|query| query.to_static())
481                .collect::<Vec<_>>()
482                .into()
483        };
484        match self {
485            Self::Any => Parameters::Any,
486            Self::Prefix(inner) => Parameters::Prefix(queries(inner)),
487            Self::Exact(inner) => Parameters::Exact(queries(inner)),
488        }
489    }
490}
491
492/// Search filter for functions in a [`crate::registry::Registry`].
493///
494/// Every field is optional and an empty query matches everything.
495///
496/// Three of the fields are a [`Filter`] rather than an `Option`, because the
497/// signature's own field is optional and a query must be able to ask for its
498/// **absence**. `type_query: Filter::Absent` asks for a free function, not a
499/// method, which an `Option` cannot express.
500///
501/// ```
502/// # use intuicio_core::{Filter, function::{FunctionQuery, Parameters}};
503/// // A free function called `add`, taking exactly two parameters.
504/// let query = FunctionQuery {
505///     name: Some("add".into()),
506///     module_name: Filter::Matching("lib".into()),
507///     type_query: Filter::Absent,
508///     inputs: Parameters::Exact(vec![Default::default(), Default::default()].into()),
509///     ..Default::default()
510/// };
511/// ```
512#[derive(Debug, Default, Clone, PartialEq, Hash)]
513pub struct FunctionQuery<'a> {
514    /// Required function name.
515    pub name: Option<Cow<'a, str>>,
516    /// Filter on the module the function belongs to.
517    pub module_name: Filter<Cow<'a, str>>,
518    /// Filter on the type the function is a method of.
519    pub type_query: Filter<TypeQuery<'a>>,
520    /// Required visibility.
521    pub visibility: Option<Visibility>,
522    /// Filter on the argument list.
523    pub inputs: Parameters<'a>,
524    /// Filter on the result list.
525    pub outputs: Parameters<'a>,
526    /// Predicate the function metadata must satisfy.
527    pub meta: Filter<FunctionMetaQuery>,
528}
529
530impl FunctionQuery<'_> {
531    /// Returns `true` when `signature` satisfies every set field.
532    pub fn is_valid(&self, signature: &FunctionSignature) -> bool {
533        self.name
534            .as_ref()
535            .map(|name| name.as_ref() == signature.name)
536            .unwrap_or(true)
537            && self
538                .module_name
539                .is_valid(signature.module_name.as_ref(), |name, module_name| {
540                    name.as_ref() == module_name
541                })
542            && self
543                .type_query
544                .is_valid(signature.type_handle.as_ref(), |query, handle| {
545                    query.is_valid(handle)
546                })
547            && self
548                .visibility
549                .map(|visibility| signature.visibility.is_visible(visibility))
550                .unwrap_or(true)
551            && self.inputs.is_valid(&signature.inputs)
552            && self.outputs.is_valid(&signature.outputs)
553            && self
554                .meta
555                .is_valid(signature.meta.as_ref(), |query, meta| query(meta))
556    }
557
558    /// Hashes the query, which is the key the registry caches results under.
559    pub fn as_hash(&self) -> u64 {
560        let mut hasher = FxHasher::default();
561        self.hash(&mut hasher);
562        hasher.finish()
563    }
564
565    /// Copies borrowed names into owned ones, so the query can outlive them.
566    pub fn to_static(&self) -> FunctionQuery<'static> {
567        FunctionQuery {
568            name: self
569                .name
570                .as_ref()
571                .map(|name| name.as_ref().to_owned().into()),
572            module_name: self.module_name.map(|name| name.as_ref().to_owned().into()),
573            type_query: self.type_query.map(|query| query.to_static()),
574            visibility: self.visibility,
575            inputs: self.inputs.to_static(),
576            outputs: self.outputs.to_static(),
577            meta: self.meta,
578        }
579    }
580}
581
582/// Builds a [`FunctionSignature`], looking every type up in a registry.
583///
584/// ```ignore
585/// function_signature! {
586///     registry => mod lib fn add(a: i32, b: i32) -> (result: i32)
587/// }
588/// ```
589///
590/// # Panics
591///
592/// Panics when a type used in the signature is not registered.
593#[macro_export]
594macro_rules! function_signature {
595    (
596        $registry:expr
597        =>
598        $(mod $module_name:ident)?
599        $(type ($type:ty))?
600        fn
601        $name:ident
602        ($( $input_name:ident : $input_type:ty ),*)
603        ->
604        ($( $output_name:ident : $output_type:ty ),*)
605    ) => {{
606        let mut result = $crate::function::FunctionSignature::new(stringify!($name));
607        $(
608            result.module_name = Some(stringify!($module_name).to_owned());
609        )?
610        $(
611            result.type_handle = Some($registry.find_type($crate::types::TypeQuery::of::<$type>()).unwrap());
612        )?
613        $(
614            result.inputs.push(
615                $crate::function::FunctionParameter::new(
616                    stringify!($input_name).to_owned(),
617                    $registry.find_type($crate::types::TypeQuery::of::<$input_type>()).unwrap()
618                )
619            );
620        )*
621        $(
622            result.outputs.push(
623                $crate::function::FunctionParameter::new(
624                    stringify!($output_name).to_owned(),
625                    $registry.find_type($crate::types::TypeQuery::of::<$output_type>()).unwrap()
626                )
627            );
628        )*
629        result
630    }};
631}
632
633/// Builds a whole [`Function`] from a signature and a Rust body.
634///
635/// The body is a block whose value is a tuple of the outputs. Arguments arrive
636/// as ordinary local variables.
637///
638/// ```ignore
639/// define_function! {
640///     registry => mod lib fn add(a: i32, b: i32) -> (result: i32) {
641///         (a + b,)
642///     }
643/// }
644/// ```
645#[macro_export]
646macro_rules! define_function {
647    (
648        $registry:expr
649        =>
650        $(mod $module_name:ident)?
651        $(type ($type:ty))?
652        fn
653        $name:ident
654        ($( $input_name:ident : $input_type:ty),*)
655        ->
656        ($( $output_name:ident : $output_type:ty),*)
657        $code:block
658    ) => {
659        $crate::function::Function::new(
660            $crate::function_signature! {
661                $registry
662                =>
663                $(mod $module_name)?
664                $(type ($type))?
665                fn
666                $name
667                ($($input_name : $input_type),*)
668                ->
669                ($($output_name : $output_type),*)
670            },
671            $crate::function::FunctionBody::closure(move |context, registry| {
672                use intuicio_data::data_stack::DataStackPack;
673                #[allow(unused_mut)]
674                let ($(mut $input_name,)*) = <($($input_type,)*)>::stack_pop(context.stack());
675                $code.stack_push_reversed(context.stack());
676            }),
677        )
678    };
679}
680
681#[cfg(test)]
682mod tests {
683    use crate as intuicio_core;
684    use crate::{context::*, function::*, registry::*, types::struct_type::*};
685    use intuicio_data;
686    use intuicio_derive::*;
687
688    #[intuicio_function(meta = "foo", args_meta(_bar = "foo"))]
689    fn function_meta(_bar: bool) {}
690
691    #[intuicio_function(name = "+", module_name = "core/ops")]
692    fn function_non_ident_name(a: i32, b: i32) -> i32 {
693        a + b
694    }
695
696    /// The three things `Option` and a bare parameter list could not ask, and
697    /// the one they got wrong.
698    #[test]
699    fn test_query_filters() {
700        let mut registry = Registry::default();
701        registry.add_type(NativeStructBuilder::new::<i32>().build());
702        let i32_type = registry.find_type(TypeQuery::of::<i32>()).unwrap();
703
704        let free = FunctionSignature::new("f")
705            .with_module_name("lib")
706            .with_input(FunctionParameter::new("a", i32_type.clone()));
707        let method = FunctionSignature::new("f")
708            .with_module_name("lib")
709            .with_type_handle(i32_type.clone())
710            .with_input(FunctionParameter::new("a", i32_type.clone()));
711
712        // Absence, which is how "a free function, not a method" is asked.
713        let query = FunctionQuery {
714            type_query: Filter::Absent,
715            ..Default::default()
716        };
717        assert!(query.is_valid(&free));
718        assert!(!query.is_valid(&method));
719
720        // Ignore, the default, still matches either - so old queries behave the
721        // way they did.
722        let query = FunctionQuery::default();
723        assert!(query.is_valid(&free));
724        assert!(query.is_valid(&method));
725
726        // Matching, on a field the signature may not have at all.
727        let query = FunctionQuery {
728            module_name: Filter::Matching("lib".into()),
729            ..Default::default()
730        };
731        assert!(query.is_valid(&free));
732        let query = FunctionQuery {
733            module_name: Filter::Matching("other".into()),
734            ..Default::default()
735        };
736        assert!(!query.is_valid(&free));
737
738        let two = FunctionSignature::new("g")
739            .with_input(FunctionParameter::new("a", i32_type.clone()))
740            .with_input(FunctionParameter::new("b", i32_type.clone()));
741
742        // Exact pins the count; prefix does not.
743        let one_filter = vec![FunctionQueryParameter::default()];
744        assert!(
745            FunctionQuery {
746                inputs: Parameters::Prefix(one_filter.to_owned().into()),
747                ..Default::default()
748            }
749            .is_valid(&two)
750        );
751        assert!(
752            !FunctionQuery {
753                inputs: Parameters::Exact(one_filter.into()),
754                ..Default::default()
755            }
756            .is_valid(&two)
757        );
758
759        // The bug: more filters than parameters used to match, because `zip`
760        // stopped at the shorter list and left the extra filter unchecked.
761        let three_filters = vec![
762            FunctionQueryParameter::default(),
763            FunctionQueryParameter::default(),
764            FunctionQueryParameter::default(),
765        ];
766        assert!(
767            !FunctionQuery {
768                inputs: Parameters::Prefix(three_filters.into()),
769                ..Default::default()
770            }
771            .is_valid(&two)
772        );
773    }
774
775    #[test]
776    fn test_function_non_ident_name() {
777        let mut registry = Registry::default();
778        registry.add_type(NativeStructBuilder::new::<i32>().build());
779        let signature = function_non_ident_name::define_signature(&registry);
780        assert_eq!(signature.name, "+");
781        assert_eq!(signature.module_name.as_deref(), Some("core/ops"));
782    }
783
784    #[test]
785    fn test_function() {
786        fn add(context: &mut Context, _: &Registry) {
787            let a = context.stack().pop::<i32>().unwrap();
788            let b = context.stack().pop::<i32>().unwrap();
789            context.stack().push(a + b);
790        }
791
792        let i32_handle = NativeStructBuilder::new::<i32>()
793            .build()
794            .into_type()
795            .into_handle();
796        let signature = FunctionSignature::new("add")
797            .with_input(FunctionParameter::new("a", i32_handle.clone()))
798            .with_input(FunctionParameter::new("b", i32_handle.clone()))
799            .with_output(FunctionParameter::new("result", i32_handle));
800        let function = Function::new(signature.to_owned(), FunctionBody::pointer(add));
801
802        assert!(FunctionQuery::default().is_valid(&signature));
803        assert!(
804            FunctionQuery {
805                name: Some("add".into()),
806                ..Default::default()
807            }
808            .is_valid(&signature)
809        );
810        assert!(
811            FunctionQuery {
812                name: Some("add".into()),
813                inputs: [
814                    FunctionQueryParameter {
815                        name: Some("a".into()),
816                        ..Default::default()
817                    },
818                    FunctionQueryParameter {
819                        name: Some("b".into()),
820                        ..Default::default()
821                    }
822                ]
823                .as_slice()
824                .into(),
825                outputs: [FunctionQueryParameter {
826                    name: Some("result".into()),
827                    ..Default::default()
828                }]
829                .as_slice()
830                .into(),
831                ..Default::default()
832            }
833            .is_valid(&signature)
834        );
835        assert!(
836            !FunctionQuery {
837                name: Some("add".into()),
838                inputs: [
839                    FunctionQueryParameter {
840                        name: Some("b".into()),
841                        ..Default::default()
842                    },
843                    FunctionQueryParameter {
844                        name: Some("a".into()),
845                        ..Default::default()
846                    }
847                ]
848                .as_slice()
849                .into(),
850                ..Default::default()
851            }
852            .is_valid(&signature)
853        );
854
855        let mut context = Context::new(10240, 10240);
856        let registry = Registry::default().with_basic_types();
857
858        context.stack().push(2);
859        context.stack().push(40);
860        function.invoke(&mut context, &registry);
861        assert_eq!(context.stack().pop::<i32>().unwrap(), 42);
862
863        assert_eq!(
864            function_meta::define_signature(&registry).meta,
865            Some(Meta::Identifier("foo".to_owned()))
866        );
867    }
868}