Avoid allocations in metadata.

This commit is contained in:
Stephen Chung
2022-12-02 16:44:45 +08:00
parent e10576abff
commit e53be6e8ed
5 changed files with 86 additions and 79 deletions

View File

@@ -9,7 +9,10 @@ use crate::types::dynamic::{DynamicWriteLock, Variant};
use crate::{reify, Dynamic, NativeCallContext, RhaiResultOf};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{any::TypeId, mem};
use std::{
any::{type_name, TypeId},
mem,
};
/// These types are used to build a unique _marker_ tuple type for each combination
/// of function parameter types in order to make each trait implementation unique.
@@ -19,13 +22,13 @@ use std::{any::TypeId, mem};
///
/// # Examples
///
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), R, ()>` = `Fn(&mut A, B, &C) -> R`
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), 3, R, ()>` = `Fn(&mut A, B, &C) -> R`
///
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), R, NativeCallContext>` = `Fn(NativeCallContext, &mut A, B, &C) -> R`
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), 3, R, NativeCallContext>` = `Fn(NativeCallContext, &mut A, B, &C) -> R`
///
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), R, RhaiResultOf<()>>` = `Fn(&mut A, B, &C) -> Result<R, Box<EvalAltResult>>`
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), 3, R, RhaiResultOf<()>>` = `Fn(&mut A, B, &C) -> Result<R, Box<EvalAltResult>>`
///
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), R, RhaiResultOf<NativeCallContext>>` = `Fn(NativeCallContext, &mut A, B, &C) -> Result<R, Box<EvalAltResult>>`
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), 3, R, RhaiResultOf<NativeCallContext>>` = `Fn(NativeCallContext, &mut A, B, &C) -> Result<R, Box<EvalAltResult>>`
///
/// These types are not actually used anywhere.
pub struct Mut<T>(T);
@@ -66,18 +69,18 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
///
/// * `ARGS` - a tuple containing parameter types, with `&mut T` represented by `Mut<T>`.
/// * `RET` - return type of the function; if the function returns `Result`, it is the unwrapped inner value type.
pub trait RegisterNativeFunction<ARGS, RET, RESULT> {
pub trait RegisterNativeFunction<ARGS, const N: usize, RET, RESULT> {
/// Convert this function into a [`CallableFunction`].
#[must_use]
fn into_callable_function(self) -> CallableFunction;
/// Get the type ID's of this function's parameters.
#[must_use]
fn param_types() -> Box<[TypeId]>;
fn param_types() -> [TypeId; N];
/// _(metadata)_ Get the type names of this function's parameters.
/// Exported under the `metadata` feature only.
#[cfg(feature = "metadata")]
#[must_use]
fn param_names() -> Box<[&'static str]>;
fn param_names() -> [&'static str; N];
/// _(metadata)_ Get the type ID of this function's return value.
/// Exported under the `metadata` feature only.
#[cfg(feature = "metadata")]
@@ -89,7 +92,7 @@ pub trait RegisterNativeFunction<ARGS, RET, RESULT> {
#[inline(always)]
#[must_use]
fn return_type_name() -> &'static str {
std::any::type_name::<RET>()
type_name::<RET>()
}
}
@@ -124,23 +127,24 @@ macro_rules! check_constant {
macro_rules! def_register {
() => {
def_register!(imp Pure :);
def_register!(imp Pure : 0;);
};
(imp $abi:ident : $($par:ident => $arg:expr => $mark:ty => $param:ty => $let:stmt => $clone:expr),*) => {
(imp $abi:ident : $n:expr ; $($par:ident => $arg:expr => $mark:ty => $param:ty => $let:stmt => $clone:expr),*) => {
// ^ function ABI type
// ^ function parameter generic type name (A, B, C etc.)
// ^ call argument(like A, *B, &mut C etc)
// ^ function parameter marker type (A, Ref<B> or Mut<C>)
// ^ function parameter actual type (A, &B or &mut C)
// ^ argument let statement
// ^ number of parameters
// ^ function parameter generic type name (A, B, C etc.)
// ^ call argument(like A, *B, &mut C etc)
// ^ function parameter marker type (A, Ref<B> or Mut<C>)
// ^ function parameter actual type (A, &B or &mut C)
// ^ argument let statement
impl<
FN: Fn($($param),*) -> RET + SendSync + 'static,
$($par: Variant + Clone,)*
RET: Variant + Clone
> RegisterNativeFunction<($($mark,)*), RET, ()> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { Box::new([$(TypeId::of::<$par>()),*]) }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { Box::new([$(std::any::type_name::<$param>()),*]) }
> RegisterNativeFunction<($($mark,)*), $n, RET, ()> for FN {
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| {
@@ -163,9 +167,9 @@ macro_rules! def_register {
FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RET + SendSync + 'static,
$($par: Variant + Clone,)*
RET: Variant + Clone
> RegisterNativeFunction<($($mark,)*), RET, NativeCallContext<'static>> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { Box::new([$(TypeId::of::<$par>()),*]) }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { Box::new([$(std::any::type_name::<$param>()),*]) }
> RegisterNativeFunction<($($mark,)*), $n, RET, NativeCallContext<'static>> for FN {
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
@@ -188,11 +192,11 @@ macro_rules! def_register {
FN: Fn($($param),*) -> RhaiResultOf<RET> + SendSync + 'static,
$($par: Variant + Clone,)*
RET: Variant + Clone
> RegisterNativeFunction<($($mark,)*), RET, RhaiResultOf<()>> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { Box::new([$(TypeId::of::<$par>()),*]) }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { Box::new([$(std::any::type_name::<$param>()),*]) }
> RegisterNativeFunction<($($mark,)*), $n, RET, RhaiResultOf<()>> for FN {
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RhaiResultOf<RET>>() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RhaiResultOf<RET>>() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types!
@@ -211,11 +215,11 @@ macro_rules! def_register {
FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RhaiResultOf<RET> + SendSync + 'static,
$($par: Variant + Clone,)*
RET: Variant + Clone
> RegisterNativeFunction<($($mark,)*), RET, RhaiResultOf<NativeCallContext<'static>>> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { Box::new([$(TypeId::of::<$par>()),*]) }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { Box::new([$(std::any::type_name::<$param>()),*]) }
> RegisterNativeFunction<($($mark,)*), $n, RET, RhaiResultOf<NativeCallContext<'static>>> for FN {
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RhaiResultOf<RET>>() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RhaiResultOf<RET>>() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types!
@@ -232,19 +236,20 @@ macro_rules! def_register {
//def_register!(imp_pop $($par => $mark => $param),*);
};
($p0:ident $(, $p:ident)*) => {
def_register!(imp Pure : $p0 => $p0 => $p0 => $p0 => let $p0 => by_value $(, $p => $p => $p => $p => let $p => by_value)*);
def_register!(imp Method : $p0 => &mut $p0 => Mut<$p0> => &mut $p0 => let mut $p0 => by_ref $(, $p => $p => $p => $p => let $p => by_value)*);
($p0:ident:$n0:expr $(, $p:ident: $n:expr)*) => {
def_register!(imp Pure : $n0 ; $p0 => $p0 => $p0 => $p0 => let $p0 => by_value $(, $p => $p => $p => $p => let $p => by_value)*);
def_register!(imp Method : $n0 ; $p0 => &mut $p0 => Mut<$p0> => &mut $p0 => let mut $p0 => by_ref $(, $p => $p => $p => $p => let $p => by_value)*);
// ^ CallableFunction constructor
// ^ first parameter passed through
// ^ others passed by value (by_value)
// ^ number of arguments
// ^ first parameter passed through
// ^ others passed by value (by_value)
// Currently does not support first argument which is a reference, as there will be
// conflicting implementations since &T: Any and T: Any cannot be distinguished
//def_register!(imp $p0 => Ref<$p0> => &$p0 => by_ref $(, $p => $p => $p => by_value)*);
def_register!($($p),*);
def_register!($($p: $n),*);
};
}
def_register!(A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, R, S, T, U, V);
def_register!(A:20, B:19, C:18, D:17, E:16, F:15, G:14, H:13, J:12, K:11, L:10, M:9, N:8, P:7, Q:6, R:5, S:4, T:3, U:2, V:1);