Use RestoreOnDrop.
This commit is contained in:
145
src/func/call.rs
145
src/func/call.rs
@@ -9,6 +9,7 @@ use crate::engine::{
|
||||
};
|
||||
use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState};
|
||||
use crate::tokenizer::{is_valid_function_name, Token};
|
||||
use crate::types::RestoreOnDrop;
|
||||
use crate::{
|
||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, Module,
|
||||
OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR,
|
||||
@@ -88,9 +89,11 @@ impl<'a> ArgBackup<'a> {
|
||||
/// If `change_first_arg_to_copy` has been called, this function **MUST** be called _BEFORE_
|
||||
/// exiting the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak.
|
||||
#[inline(always)]
|
||||
pub fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) {
|
||||
pub fn restore_first_arg(&mut self, args: &mut FnCallArgs<'a>) {
|
||||
if let Some(p) = self.orig_mut.take() {
|
||||
args[0] = p;
|
||||
} else {
|
||||
unreachable!("`Some`");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,7 +330,7 @@ impl Engine {
|
||||
name: &str,
|
||||
op_token: Option<&Token>,
|
||||
hash: u64,
|
||||
args: &mut FnCallArgs,
|
||||
mut args: &mut FnCallArgs,
|
||||
is_ref_mut: bool,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||
@@ -354,48 +357,45 @@ impl Engine {
|
||||
#[cfg(feature = "debugging")]
|
||||
let orig_call_stack_len = global.debugger.call_stack().len();
|
||||
|
||||
let mut _result = if let Some(FnResolutionCacheEntry { func, source }) = func {
|
||||
assert!(func.is_native());
|
||||
let FnResolutionCacheEntry { func, source } = func.unwrap();
|
||||
assert!(func.is_native());
|
||||
|
||||
let mut backup = ArgBackup::new();
|
||||
let backup = &mut ArgBackup::new();
|
||||
|
||||
// Calling pure function but the first argument is a reference?
|
||||
if is_ref_mut && func.is_pure() && !args.is_empty() {
|
||||
// Clone the first argument
|
||||
backup.change_first_arg_to_copy(args);
|
||||
}
|
||||
// Calling pure function but the first argument is a reference?
|
||||
let swap = is_ref_mut && func.is_pure() && !args.is_empty();
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.push_call_stack_frame(
|
||||
self.get_interned_string(name),
|
||||
args.iter().map(|v| (*v).clone()).collect(),
|
||||
source.clone().or_else(|| global.source.clone()),
|
||||
pos,
|
||||
);
|
||||
}
|
||||
if swap {
|
||||
// Clone the first argument
|
||||
backup.change_first_arg_to_copy(args);
|
||||
}
|
||||
|
||||
// Run external function
|
||||
let src = source.as_ref().map(|s| s.as_str());
|
||||
let context = (self, name, src, &*global, lib, pos, level).into();
|
||||
let args =
|
||||
&mut *RestoreOnDrop::new_if(swap, &mut args, move |a| backup.restore_first_arg(a));
|
||||
|
||||
let result = if func.is_plugin_fn() {
|
||||
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)
|
||||
}
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.push_call_stack_frame(
|
||||
self.get_interned_string(name),
|
||||
args.iter().map(|v| (*v).clone()).collect(),
|
||||
source.clone().or_else(|| global.source.clone()),
|
||||
pos,
|
||||
);
|
||||
}
|
||||
|
||||
// Run external function
|
||||
let src = source.as_ref().map(|s| s.as_str());
|
||||
let context = (self, name, src, &*global, lib, pos, level).into();
|
||||
|
||||
let mut _result = if func.is_plugin_fn() {
|
||||
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 {
|
||||
func.get_native_fn().unwrap()(context, args)
|
||||
};
|
||||
|
||||
// Restore the original reference
|
||||
backup.restore_first_arg(args);
|
||||
|
||||
result
|
||||
f.call(context, args)
|
||||
}
|
||||
} else {
|
||||
unreachable!("`Some`");
|
||||
func.get_native_fn().unwrap()(context, args)
|
||||
};
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
@@ -413,11 +413,11 @@ impl Engine {
|
||||
Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r),
|
||||
Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err),
|
||||
};
|
||||
match self
|
||||
|
||||
if let Err(err) = self
|
||||
.run_debugger_raw(global, caches, lib, level, scope, &mut None, node, event)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(err) => _result = Err(err),
|
||||
_result = Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,7 +543,7 @@ impl Engine {
|
||||
fn_name: &str,
|
||||
op_token: Option<&Token>,
|
||||
hashes: FnCallHashes,
|
||||
args: &mut FnCallArgs,
|
||||
mut args: &mut FnCallArgs,
|
||||
is_ref_mut: bool,
|
||||
_is_method_call: bool,
|
||||
pos: Position,
|
||||
@@ -613,19 +613,11 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if !hashes.is_native_only() {
|
||||
// Script-defined function call?
|
||||
let hash = hashes.script();
|
||||
let local_entry = &mut None;
|
||||
|
||||
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
||||
.resolve_fn(
|
||||
global,
|
||||
caches,
|
||||
local_entry,
|
||||
lib,
|
||||
None,
|
||||
hashes.script(),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.resolve_fn(global, caches, local_entry, lib, None, hash, None, false)
|
||||
.cloned()
|
||||
{
|
||||
// Script function call
|
||||
@@ -647,8 +639,9 @@ impl Engine {
|
||||
};
|
||||
|
||||
let orig_source = mem::replace(&mut global.source, source.clone());
|
||||
let global = &mut *RestoreOnDrop::new(global, move |g| g.source = orig_source);
|
||||
|
||||
let result = if _is_method_call {
|
||||
return if _is_method_call {
|
||||
// Method call of script function - map first argument to `this`
|
||||
let (first_arg, rest_args) = args.split_first_mut().unwrap();
|
||||
|
||||
@@ -666,27 +659,24 @@ impl Engine {
|
||||
)
|
||||
} else {
|
||||
// Normal call of script function
|
||||
let mut backup = ArgBackup::new();
|
||||
let backup = &mut ArgBackup::new();
|
||||
|
||||
// The first argument is a reference?
|
||||
if is_ref_mut && !args.is_empty() {
|
||||
let swap = is_ref_mut && !args.is_empty();
|
||||
|
||||
if swap {
|
||||
backup.change_first_arg_to_copy(args);
|
||||
}
|
||||
|
||||
let result = self.call_script_fn(
|
||||
let args = &mut *RestoreOnDrop::new_if(swap, &mut args, move |a| {
|
||||
backup.restore_first_arg(a)
|
||||
});
|
||||
|
||||
self.call_script_fn(
|
||||
global, caches, lib, level, scope, &mut None, func, args, true, pos,
|
||||
);
|
||||
|
||||
// Restore the original reference
|
||||
backup.restore_first_arg(args);
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
// Restore the original source
|
||||
global.source = orig_source;
|
||||
|
||||
return result.map(|r| (r, false));
|
||||
)
|
||||
}
|
||||
.map(|r| (r, false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,17 +712,14 @@ impl Engine {
|
||||
|
||||
// Do not match function exit for arguments
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset_debugger = global.debugger.clear_status_if(|status| {
|
||||
let reset = global.debugger.clear_status_if(|status| {
|
||||
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
|
||||
});
|
||||
|
||||
let result = self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr);
|
||||
|
||||
// Restore function exit status
|
||||
#[cfg(feature = "debugging")]
|
||||
global.debugger.reset_status(reset_debugger);
|
||||
let global = &mut *RestoreOnDrop::new(global, move |g| g.debugger.reset_status(reset));
|
||||
|
||||
result.map(|r| (r, arg_expr.start_position()))
|
||||
self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr)
|
||||
.map(|r| (r, arg_expr.start_position()))
|
||||
}
|
||||
|
||||
/// Call a dot method.
|
||||
@@ -1389,15 +1376,13 @@ impl Engine {
|
||||
Some(f) if f.is_script() => {
|
||||
let fn_def = f.get_script_fn_def().expect("script-defined function");
|
||||
let new_scope = &mut Scope::new();
|
||||
|
||||
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
|
||||
let global = &mut *RestoreOnDrop::new(global, move |g| g.source = orig_source);
|
||||
|
||||
let result = self.call_script_fn(
|
||||
self.call_script_fn(
|
||||
global, caches, lib, level, new_scope, &mut None, fn_def, &mut args, true, pos,
|
||||
);
|
||||
|
||||
global.source = orig_source;
|
||||
|
||||
result
|
||||
)
|
||||
}
|
||||
|
||||
Some(f) if f.is_plugin_fn() => {
|
||||
|
Reference in New Issue
Block a user