#![cfg(not(feature = "no_function"))]
use super::call::FnCallArgs;
use crate::ast::ScriptFnDef;
use crate::eval::{Caches, GlobalRuntimeState};
use crate::func::EncapsulatedEnviron;
use crate::{Dynamic, Engine, Position, RhaiResult, Scope, ERR};
use std::mem;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
impl Engine {
pub(crate) fn call_script_fn(
&self,
global: &mut GlobalRuntimeState,
caches: &mut Caches,
scope: &mut Scope,
mut this_ptr: Option<&mut Dynamic>,
_environ: Option<&EncapsulatedEnviron>,
fn_def: &ScriptFnDef,
args: &mut FnCallArgs,
rewind_scope: bool,
pos: Position,
) -> RhaiResult {
assert_eq!(fn_def.params.len(), args.len());
self.track_operation(global, pos)?;
if global.level > self.max_call_levels() {
return Err(ERR::ErrorStackOverflow(pos).into());
}
#[cfg(feature = "debugging")]
if self.debugger_interface.is_none() && fn_def.body.is_empty() {
return Ok(Dynamic::UNIT);
}
#[cfg(not(feature = "debugging"))]
if fn_def.body.is_empty() {
return Ok(Dynamic::UNIT);
}
let orig_scope_len = scope.len();
let orig_lib_len = global.lib.len();
#[cfg(not(feature = "no_module"))]
let orig_imports_len = global.num_imports();
#[cfg(feature = "debugging")]
let orig_call_stack_len = global
.debugger
.as_ref()
.map_or(0, |dbg| dbg.call_stack().len());
scope.extend(fn_def.params.iter().cloned().zip(args.iter_mut().map(|v| {
mem::take(*v)
})));
#[cfg(feature = "debugging")]
if self.is_debugger_registered() {
let fn_name = fn_def.name.clone();
let args = scope.iter().skip(orig_scope_len).map(|(.., v)| v).collect();
let source = global.source.clone();
global
.debugger_mut()
.push_call_stack_frame(fn_name, args, source, pos);
}
let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len();
#[cfg(not(feature = "no_module"))]
let orig_constants = if let Some(environ) = _environ {
let EncapsulatedEnviron {
lib,
imports,
constants,
} = environ;
imports
.iter()
.cloned()
.for_each(|(n, m)| global.push_import(n, m));
global.lib.push(lib.clone());
Some(mem::replace(&mut global.constants, constants.clone()))
} else {
None
};
#[cfg(feature = "debugging")]
if self.is_debugger_registered() {
let node = crate::ast::Stmt::Noop(fn_def.body.position());
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), &node)?;
}
let mut _result: RhaiResult = self
.eval_stmt_block(
global,
caches,
scope,
this_ptr.as_deref_mut(),
&fn_def.body,
rewind_scope,
)
.or_else(|err| match *err {
ERR::Return(x, ..) => Ok(x),
mut err if err.is_system_exception() => {
err.set_position(pos);
Err(err.into())
}
_ => Err(ERR::ErrorInFunctionCall(
fn_def.name.to_string(),
#[cfg(not(feature = "no_module"))]
_environ
.and_then(|environ| environ.lib.id())
.unwrap_or_else(|| global.source().unwrap_or(""))
.to_string(),
#[cfg(feature = "no_module")]
global.source().unwrap_or("").to_string(),
err,
pos,
)
.into()),
});
#[cfg(feature = "debugging")]
if self.is_debugger_registered() {
let trigger = match global.debugger_mut().status {
crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level,
crate::eval::DebuggerStatus::Next(.., true) => true,
_ => false,
};
if trigger {
let node = crate::ast::Stmt::Noop(fn_def.body.end_position().or_else(pos));
let node = (&node).into();
let event = match _result {
Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r),
Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err),
};
match self.run_debugger_raw(global, caches, scope, this_ptr, node, event) {
Ok(_) => (),
Err(err) => _result = Err(err),
}
}
global
.debugger
.as_mut()
.unwrap()
.rewind_call_stack(orig_call_stack_len);
}
if rewind_scope {
scope.rewind(orig_scope_len);
} else if !args.is_empty() {
scope.remove_range(orig_scope_len, args.len());
}
global.lib.truncate(orig_lib_len);
#[cfg(not(feature = "no_module"))]
global.truncate_imports(orig_imports_len);
#[cfg(not(feature = "no_module"))]
if let Some(constants) = orig_constants {
global.constants = constants;
}
caches.rewind_fn_resolution_caches(orig_fn_resolution_caches_len);
_result
}
#[must_use]
pub(crate) fn has_script_fn(
&self,
global: &GlobalRuntimeState,
caches: &mut Caches,
hash_script: u64,
) -> bool {
let cache = caches.fn_resolution_cache_mut();
if let Some(result) = cache.map.get(&hash_script).map(Option::is_some) {
return result;
}
let r = global.lib.iter().any(|m| m.contains_fn(hash_script))
|| self.global_modules.iter().any(|m| m.contains_fn(hash_script));
#[cfg(not(feature = "no_module"))]
let r = r ||
global.contains_qualified_fn(hash_script)
|| self.global_sub_modules.as_deref().map_or(false, |m| {
m.values().any(|m| m.contains_qualified_fn(hash_script))
});
if !r && !cache.filter.is_absent_and_set(hash_script) {
cache.map.insert(hash_script, None);
}
r
}
}