Encode pure in CallableFunction variant.
This commit is contained in:
parent
8662ffec62
commit
407d376a61
@ -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,
|
||||
|
@ -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() => {
|
||||
|
@ -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,
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user