Encode pure in CallableFunction variant.

This commit is contained in:
Stephen Chung 2023-04-11 10:26:23 +08:00
parent 8662ffec62
commit 407d376a61
5 changed files with 47 additions and 60 deletions

View File

@ -90,15 +90,15 @@ impl Engine {
let param_type_names: Option<&[&str]> = None;
let fn_name = name.as_ref();
let no_const = false;
let is_pure = true;
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
let no_const = no_const || (F::num_params() == 3 && fn_name == crate::engine::FN_IDX_SET);
let is_pure = is_pure && (F::num_params() != 3 || fn_name != crate::engine::FN_IDX_SET);
#[cfg(not(feature = "no_object"))]
let no_const =
no_const || (F::num_params() == 2 && fn_name.starts_with(crate::engine::FN_SET));
let is_pure =
is_pure && (F::num_params() != 2 || !fn_name.starts_with(crate::engine::FN_SET));
let func = func.into_callable_function(fn_name.into(), no_const);
let func = func.into_callable_function(fn_name.into(), is_pure);
self.global_namespace_mut().set_fn(
name,

View File

@ -276,6 +276,7 @@ impl Engine {
func: CallableFunction::Method {
func: Shared::new(f),
has_context,
is_pure: false,
},
source: None,
})
@ -285,6 +286,7 @@ impl Engine {
func: CallableFunction::Method {
func: Shared::new(f),
has_context,
is_pure: true,
},
source: None,
}),
@ -372,8 +374,8 @@ impl Engine {
let backup = &mut ArgBackup::new();
// Calling pure function but the first argument is a reference?
let swap = is_ref_mut && func.is_pure() && !args.is_empty();
// Calling non-method function but the first argument is a reference?
let swap = is_ref_mut && !func.is_method() && !args.is_empty();
if swap {
// Clone the first argument
@ -400,12 +402,11 @@ impl Engine {
.has_context()
.then(|| (self, name, src, &*global, pos).into());
let mut _result = if let Some(f) = func.get_plugin_fn() {
if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into())
} else {
f.call(context, args)
}
let mut _result = if !func.is_pure() && !args.is_empty() && args[0].is_read_only() {
// If function is not pure, there must be at least one argument
Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into())
} else if let Some(f) = func.get_plugin_fn() {
f.call(context, args)
} else if let Some(f) = func.get_native_fn() {
f(context, args)
} else {
@ -1493,17 +1494,18 @@ impl Engine {
self.call_script_fn(global, caches, scope, None, environ, f, args, true, pos)
}
Some(f) if !f.is_pure() && args[0].is_read_only() => {
// If function is not pure, there must be at least one argument
Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into())
}
Some(f) if f.is_plugin_fn() => {
let f = f.get_plugin_fn().expect("plugin function");
let context = f
.has_context()
.then(|| (self, fn_name, module.id(), &*global, pos).into());
if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into())
} else {
f.call(context, args)
.and_then(|r| self.check_data_size(r, pos))
}
f.call(context, args)
.and_then(|r| self.check_data_size(r, pos))
}
Some(f) if f.is_native() => {

View File

@ -39,6 +39,8 @@ pub enum CallableFunction {
func: Shared<FnAny>,
/// Does the function take a [`NativeCallContext`][crate::NativeCallContext] parameter?
has_context: bool,
/// This is a dummy field and is not used.
is_pure: bool,
},
/// A native Rust object method with the first argument passed by reference,
/// and the rest passed by value.
@ -47,6 +49,8 @@ pub enum CallableFunction {
func: Shared<FnAny>,
/// Does the function take a [`NativeCallContext`][crate::NativeCallContext] parameter?
has_context: bool,
/// Allow operating on constants?
is_pure: bool,
},
/// An iterator function.
Iterator {
@ -105,9 +109,10 @@ impl CallableFunction {
pub fn is_pure(&self) -> bool {
match self {
Self::Pure { .. } => true,
Self::Method { .. } | Self::Iterator { .. } => false,
Self::Method { is_pure, .. } => *is_pure,
Self::Iterator { .. } => true,
Self::Plugin { func, .. } => !func.is_method_call(),
Self::Plugin { func, .. } => func.is_pure(),
#[cfg(not(feature = "no_function"))]
Self::Script { .. } => false,

View File

@ -84,7 +84,7 @@ pub trait RegisterNativeFunction<
{
/// Convert this function into a [`CallableFunction`].
#[must_use]
fn into_callable_function(self, name: Identifier, no_const: bool) -> CallableFunction;
fn into_callable_function(self, name: Identifier, is_pure: bool) -> CallableFunction;
/// Get the type ID's of this function's parameters.
#[must_use]
fn param_types() -> [TypeId; N];
@ -127,19 +127,6 @@ pub trait RegisterNativeFunction<
}
}
macro_rules! check_constant {
($abi:ident, $n:expr, $fn_name:ident, $no_const:ident, $args:ident) => {
#[cfg(any(not(feature = "no_object"), not(feature = "no_index")))]
if stringify!($abi) == "Method" && $no_const && $args[0].is_read_only() {
return Err(crate::ERR::ErrorNonPureMethodCallOnConstant(
$fn_name.to_string(),
crate::Position::NONE,
)
.into());
}
};
}
macro_rules! def_register {
() => {
def_register!(imp Pure : 0;);
@ -160,11 +147,9 @@ macro_rules! def_register {
> RegisterNativeFunction<($($mark,)*), $n, false, RET, false> 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>()),*] }
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, is_pure: bool) -> CallableFunction {
CallableFunction::$abi { func: Shared::new(move |_, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types!
check_constant!($abi, $n, fn_name, no_const, args);
let mut drain = args.iter_mut();
$(let mut $par = $clone(drain.next().unwrap()); )*
@ -173,7 +158,7 @@ macro_rules! def_register {
// Map the result
Ok(Dynamic::from(r))
}), has_context: false }
}), has_context: false, is_pure }
}
}
@ -184,13 +169,11 @@ macro_rules! def_register {
> RegisterNativeFunction<($($mark,)*), $n, true, RET, false> 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>()),*] }
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, is_pure: bool) -> CallableFunction {
CallableFunction::$abi { func: Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
let ctx = ctx.unwrap();
// The arguments are assumed to be of the correct number and types!
check_constant!($abi, $n, fn_name, no_const, args);
let mut drain = args.iter_mut();
$(let mut $par = $clone(drain.next().unwrap()); )*
@ -199,7 +182,7 @@ macro_rules! def_register {
// Map the result
Ok(Dynamic::from(r))
}), has_context: true }
}), has_context: true, is_pure }
}
}
@ -211,17 +194,15 @@ macro_rules! def_register {
#[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_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, is_pure: bool) -> CallableFunction {
CallableFunction::$abi { func: Shared::new(move |_, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types!
check_constant!($abi, $n, fn_name, no_const, args);
let mut drain = args.iter_mut();
$(let mut $par = $clone(drain.next().unwrap()); )*
// Call the function with each argument value
self($($arg),*).map(Dynamic::from)
}), has_context: false }
}), has_context: false, is_pure }
}
}
@ -233,19 +214,17 @@ macro_rules! def_register {
#[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_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, is_pure: bool) -> CallableFunction {
CallableFunction::$abi { func: Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
let ctx = ctx.unwrap();
// The arguments are assumed to be of the correct number and types!
check_constant!($abi, $n, fn_name, no_const, args);
let mut drain = args.iter_mut();
$(let mut $par = $clone(drain.next().unwrap()); )*
// Call the function with each argument value
self(ctx, $($arg),*).map(Dynamic::from)
}), has_context: true }
}), has_context: true, is_pure }
}
}

View File

@ -1266,6 +1266,7 @@ impl Module {
CallableFunction::Method {
func: Shared::new(f),
has_context: true,
is_pure: false,
},
)
}
@ -1303,15 +1304,15 @@ impl Module {
F: RegisterNativeFunction<A, N, C, T, true>,
{
let fn_name = name.into();
let no_const = false;
let is_pure = true;
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
let no_const = no_const || (F::num_params() == 3 && fn_name == crate::engine::FN_IDX_SET);
let is_pure = is_pure && (F::num_params() != 3 || fn_name != crate::engine::FN_IDX_SET);
#[cfg(not(feature = "no_object"))]
let no_const =
no_const || (F::num_params() == 2 && fn_name.starts_with(crate::engine::FN_SET));
let is_pure =
is_pure && (F::num_params() != 2 || !fn_name.starts_with(crate::engine::FN_SET));
let func = func.into_callable_function(fn_name.clone(), no_const);
let func = func.into_callable_function(fn_name.clone(), is_pure);
self.set_fn(
fn_name,
@ -1350,7 +1351,7 @@ impl Module {
F: RegisterNativeFunction<(Mut<A>,), 1, C, T, true> + SendSync + 'static,
{
let fn_name = crate::engine::make_getter(name.as_ref());
let func = func.into_callable_function(fn_name.clone(), false);
let func = func.into_callable_function(fn_name.clone(), true);
self.set_fn(
fn_name,
@ -1394,7 +1395,7 @@ impl Module {
F: RegisterNativeFunction<(Mut<A>, T), 2, C, (), true> + SendSync + 'static,
{
let fn_name = crate::engine::make_setter(name.as_ref());
let func = func.into_callable_function(fn_name.clone(), true);
let func = func.into_callable_function(fn_name.clone(), false);
self.set_fn(
fn_name,
@ -1510,7 +1511,7 @@ impl Module {
FnAccess::Public,
None,
F::param_types(),
func.into_callable_function(crate::engine::FN_IDX_GET.into(), false),
func.into_callable_function(crate::engine::FN_IDX_GET.into(), true),
)
}
@ -1571,7 +1572,7 @@ impl Module {
FnAccess::Public,
None,
F::param_types(),
func.into_callable_function(crate::engine::FN_IDX_SET.into(), true),
func.into_callable_function(crate::engine::FN_IDX_SET.into(), false),
)
}