Move pure checking out of functions.
This commit is contained in:
@@ -418,7 +418,12 @@ impl Engine {
|
||||
let context = (self, name, source, &*global, lib, pos, level).into();
|
||||
|
||||
let result = if func.is_plugin_fn() {
|
||||
func.get_plugin_fn().unwrap().call(context, args)
|
||||
let f = func.get_plugin_fn().unwrap();
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
func.get_native_fn().unwrap()(context, args)
|
||||
};
|
||||
@@ -1229,13 +1234,9 @@ impl Engine {
|
||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||
})?;
|
||||
|
||||
let (mut target, _pos) =
|
||||
let (target, _pos) =
|
||||
self.search_namespace(scope, global, lib, this_ptr, first_expr, level)?;
|
||||
|
||||
if target.is_read_only() {
|
||||
target = target.into_owned();
|
||||
}
|
||||
|
||||
self.track_operation(global, _pos)?;
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
@@ -1425,11 +1426,12 @@ impl Engine {
|
||||
|
||||
Some(f) if f.is_plugin_fn() => {
|
||||
let context = (self, fn_name, module.id(), &*global, lib, pos, level).into();
|
||||
let result = f
|
||||
.get_plugin_fn()
|
||||
.expect("plugin function")
|
||||
.clone()
|
||||
.call(context, &mut args);
|
||||
let f = f.get_plugin_fn().expect("plugin function");
|
||||
let result = 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, &mut args)
|
||||
};
|
||||
self.check_return_value(result, pos)
|
||||
}
|
||||
|
||||
|
@@ -29,4 +29,14 @@ pub trait PluginFunction {
|
||||
/// Is this plugin function a method?
|
||||
#[must_use]
|
||||
fn is_method_call(&self) -> bool;
|
||||
|
||||
/// Is this plugin function pure?
|
||||
///
|
||||
/// This defaults to `true` such that any old implementation that has constant-checking code
|
||||
/// inside the function itself will continue to work.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn is_pure(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@@ -96,26 +96,27 @@ pub trait RegisterNativeFunction<ARGS, RET, RESULT> {
|
||||
const EXPECT_ARGS: &str = "arguments";
|
||||
|
||||
macro_rules! check_constant {
|
||||
($ctx:ident, $args:ident) => {
|
||||
($abi:ident, $ctx:ident, $args:ident) => {
|
||||
#[cfg(any(not(feature = "no_object"), not(feature = "no_index")))]
|
||||
{
|
||||
let args_len = $args.len();
|
||||
|
||||
if args_len > 0 && $args[0].is_read_only() {
|
||||
let deny = match $ctx.fn_name() {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
f if args_len == 2 && f.starts_with(crate::engine::FN_SET) => true,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
crate::engine::FN_IDX_SET if args_len == 3 => true,
|
||||
_ => false,
|
||||
};
|
||||
if deny {
|
||||
return Err(crate::ERR::ErrorAssignmentToConstant(
|
||||
String::new(),
|
||||
crate::Position::NONE,
|
||||
)
|
||||
.into());
|
||||
if stringify!($abi) == "Method" && !$args.is_empty() {
|
||||
let deny = match $args.len() {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
3 if $ctx.fn_name() == crate::engine::FN_IDX_SET && $args[0].is_read_only() => true,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
2 if $ctx.fn_name().starts_with(crate::engine::FN_SET)
|
||||
&& $args[0].is_read_only() =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if deny {
|
||||
return Err(crate::ERR::ErrorNonPureMethodCallOnConstant(
|
||||
$ctx.fn_name().to_string(),
|
||||
crate::Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -144,7 +145,7 @@ macro_rules! def_register {
|
||||
#[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!
|
||||
check_constant!(_ctx, args);
|
||||
check_constant!($abi, _ctx, args);
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
@@ -169,7 +170,7 @@ macro_rules! def_register {
|
||||
#[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!
|
||||
check_constant!(ctx, args);
|
||||
check_constant!($abi, ctx, args);
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
@@ -195,7 +196,7 @@ macro_rules! def_register {
|
||||
#[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!
|
||||
check_constant!(_ctx, args);
|
||||
check_constant!($abi, _ctx, args);
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
@@ -218,7 +219,7 @@ macro_rules! def_register {
|
||||
#[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!
|
||||
check_constant!(ctx, args);
|
||||
check_constant!($abi, ctx, args);
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
|
Reference in New Issue
Block a user