use super::call::FnCallArgs;
use crate::ast::FnCallHashes;
use crate::eval::{Caches, GlobalRuntimeState};
use crate::plugin::PluginFunc;
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
use crate::types::dynamic::Variant;
use crate::{
calc_fn_hash, expose_under_internals, Dynamic, Engine, EvalContext, FnArgsVec, FuncArgs,
Position, RhaiResult, RhaiResultOf, StaticVec, VarDefInfo, ERR,
};
use std::any::type_name;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
#[cfg(feature = "sync")]
pub trait SendSync: Send + Sync {}
#[cfg(feature = "sync")]
impl<T: Send + Sync> SendSync for T {}
#[cfg(not(feature = "sync"))]
pub trait SendSync {}
#[cfg(not(feature = "sync"))]
impl<T> SendSync for T {}
#[cfg(not(feature = "sync"))]
pub use alloc::rc::Rc as Shared;
#[cfg(feature = "sync")]
pub use alloc::sync::Arc as Shared;
#[cfg(not(feature = "sync"))]
pub use std::cell::RefCell as Locked;
#[cfg(not(feature = "sync"))]
pub type LockGuard<'a, T> = std::cell::Ref<'a, T>;
#[cfg(not(feature = "sync"))]
pub type LockGuardMut<'a, T> = std::cell::RefMut<'a, T>;
#[cfg(feature = "sync")]
#[allow(dead_code)]
pub use std::sync::RwLock as Locked;
#[cfg(feature = "sync")]
#[allow(dead_code)]
pub type LockGuard<'a, T> = std::sync::RwLockReadGuard<'a, T>;
#[cfg(feature = "sync")]
#[allow(dead_code)]
pub type LockGuardMut<'a, T> = std::sync::RwLockWriteGuard<'a, T>;
#[derive(Debug)]
pub struct NativeCallContext<'a> {
engine: &'a Engine,
fn_name: &'a str,
source: Option<&'a str>,
global: &'a GlobalRuntimeState,
pos: Position,
}
#[deprecated = "This type is NOT deprecated, but it is considered volatile and may change in the future."]
#[cfg(feature = "internals")]
#[derive(Debug, Clone)]
pub struct NativeCallContextStore {
pub fn_name: String,
pub source: Option<String>,
pub global: GlobalRuntimeState,
pub pos: Position,
}
#[cfg(feature = "internals")]
#[allow(deprecated)]
impl NativeCallContextStore {
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline(always)]
#[must_use]
pub fn create_context<'a>(&'a self, engine: &'a Engine) -> NativeCallContext<'a> {
NativeCallContext::from_stored_data(engine, self)
}
}
impl<'a>
From<(
&'a Engine,
&'a str,
Option<&'a str>,
&'a GlobalRuntimeState,
Position,
)> for NativeCallContext<'a>
{
#[inline(always)]
fn from(
value: (
&'a Engine,
&'a str,
Option<&'a str>,
&'a GlobalRuntimeState,
Position,
),
) -> Self {
Self {
engine: value.0,
fn_name: value.1,
source: value.2,
global: value.3,
pos: value.4,
}
}
}
impl<'a> NativeCallContext<'a> {
#[cfg(feature = "internals")]
#[cfg(not(feature = "no_module"))]
#[inline(always)]
#[must_use]
pub const fn new_with_all_fields(
engine: &'a Engine,
fn_name: &'a str,
source: Option<&'a str>,
global: &'a GlobalRuntimeState,
pos: Position,
) -> Self {
Self {
engine,
fn_name,
source,
global,
pos,
}
}
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[cfg(feature = "internals")]
#[inline]
#[must_use]
#[allow(deprecated)]
pub fn from_stored_data(engine: &'a Engine, context: &'a NativeCallContextStore) -> Self {
Self {
engine,
fn_name: &context.fn_name,
source: context.source.as_deref(),
global: &context.global,
pos: context.pos,
}
}
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[cfg(feature = "internals")]
#[inline]
#[must_use]
#[allow(deprecated)]
pub fn store_data(&self) -> NativeCallContextStore {
NativeCallContextStore {
fn_name: self.fn_name.to_string(),
source: self.source.map(ToString::to_string),
global: self.global.clone(),
pos: self.pos,
}
}
#[inline(always)]
#[must_use]
pub const fn engine(&self) -> &Engine {
self.engine
}
#[inline(always)]
#[must_use]
pub const fn fn_name(&self) -> &str {
self.fn_name
}
#[inline(always)]
#[must_use]
pub const fn fn_source(&self) -> Option<&str> {
self.source
}
#[inline(always)]
#[must_use]
pub fn call_source(&self) -> Option<&str> {
self.global.source.as_deref()
}
#[inline(always)]
#[must_use]
pub const fn call_position(&self) -> Position {
self.pos
}
#[inline(always)]
#[must_use]
pub const fn call_level(&self) -> usize {
self.global.level
}
#[inline(always)]
#[must_use]
pub const fn tag(&self) -> Option<&Dynamic> {
Some(&self.global.tag)
}
#[cfg(not(feature = "no_module"))]
#[inline]
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
self.global.iter_imports()
}
#[expose_under_internals]
#[inline(always)]
#[must_use]
const fn global_runtime_state(&self) -> &GlobalRuntimeState {
self.global
}
#[cfg(not(feature = "no_function"))]
#[inline]
pub fn iter_namespaces(&self) -> impl Iterator<Item = &crate::Module> {
self.global.lib.iter().map(<_>::as_ref)
}
#[cfg(not(feature = "no_function"))]
#[cfg(feature = "internals")]
#[inline(always)]
#[must_use]
pub fn namespaces(&self) -> &[crate::SharedModule] {
&self.global.lib
}
#[inline]
pub fn call_fn<T: Variant + Clone>(
&self,
fn_name: impl AsRef<str>,
args: impl FuncArgs,
) -> RhaiResultOf<T> {
let mut arg_values = StaticVec::new_const();
args.parse(&mut arg_values);
let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();
self._call_fn_raw(fn_name, args, false, false, false)
.and_then(|result| {
result.try_cast_result().map_err(|r| {
let result_type = self.engine().map_type_name(r.type_name());
let cast_type = match type_name::<T>() {
typ if typ.contains("::") => self.engine.map_type_name(typ),
typ => typ,
};
ERR::ErrorMismatchOutputType(
cast_type.into(),
result_type.into(),
self.call_position(),
)
.into()
})
})
}
#[inline]
pub fn call_native_fn<T: Variant + Clone>(
&self,
fn_name: impl AsRef<str>,
args: impl FuncArgs,
) -> RhaiResultOf<T> {
let mut arg_values = StaticVec::new_const();
args.parse(&mut arg_values);
let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();
self._call_fn_raw(fn_name, args, true, false, false)
.and_then(|result| {
result.try_cast_result().map_err(|r| {
let result_type = self.engine().map_type_name(r.type_name());
let cast_type = match type_name::<T>() {
typ if typ.contains("::") => self.engine.map_type_name(typ),
typ => typ,
};
ERR::ErrorMismatchOutputType(
cast_type.into(),
result_type.into(),
self.call_position(),
)
.into()
})
})
}
#[inline(always)]
pub fn call_fn_raw(
&self,
fn_name: impl AsRef<str>,
is_ref_mut: bool,
is_method_call: bool,
args: &mut [&mut Dynamic],
) -> RhaiResult {
let name = fn_name.as_ref();
let native_only = !is_valid_function_name(name);
#[cfg(not(feature = "no_function"))]
let native_only = native_only && !crate::parser::is_anonymous_fn(name);
self._call_fn_raw(fn_name, args, native_only, is_ref_mut, is_method_call)
}
#[inline(always)]
pub fn call_native_fn_raw(
&self,
fn_name: impl AsRef<str>,
is_ref_mut: bool,
args: &mut [&mut Dynamic],
) -> RhaiResult {
self._call_fn_raw(fn_name, args, true, is_ref_mut, false)
}
fn _call_fn_raw(
&self,
fn_name: impl AsRef<str>,
args: &mut [&mut Dynamic],
native_only: bool,
is_ref_mut: bool,
is_method_call: bool,
) -> RhaiResult {
let global = &mut self.global.clone();
global.level += 1;
let caches = &mut Caches::new();
let fn_name = fn_name.as_ref();
let op_token = Token::lookup_symbol_from_syntax(fn_name);
let args_len = args.len();
if native_only {
return self
.engine()
.exec_native_fn_call(
global,
caches,
fn_name,
op_token.as_ref(),
calc_fn_hash(None, fn_name, args_len),
args,
is_ref_mut,
false,
self.call_position(),
)
.map(|(r, ..)| r);
}
let hash = match is_method_call {
#[cfg(not(feature = "no_function"))]
true => FnCallHashes::from_script_and_native(
calc_fn_hash(None, fn_name, args_len - 1),
calc_fn_hash(None, fn_name, args_len),
),
#[cfg(feature = "no_function")]
true => FnCallHashes::from_native_only(calc_fn_hash(None, fn_name, args_len)),
_ => FnCallHashes::from_hash(calc_fn_hash(None, fn_name, args_len)),
};
self.engine()
.exec_fn_call(
global,
caches,
None,
fn_name,
op_token.as_ref(),
hash,
args,
is_ref_mut,
is_method_call,
self.call_position(),
)
.map(|(r, ..)| r)
}
}
#[inline(always)]
#[must_use]
#[allow(dead_code)]
pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
Shared::make_mut(value)
}
#[inline(always)]
#[must_use]
#[allow(dead_code)]
pub fn shared_get_mut<T: Clone>(value: &mut Shared<T>) -> Option<&mut T> {
Shared::get_mut(value)
}
#[inline]
#[must_use]
#[allow(dead_code)]
pub fn shared_take_or_clone<T: Clone>(value: Shared<T>) -> T {
shared_try_take(value).unwrap_or_else(|v| v.as_ref().clone())
}
#[inline(always)]
#[allow(dead_code)]
pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
Shared::try_unwrap(value)
}
#[inline]
#[must_use]
#[allow(dead_code)]
pub fn shared_take<T>(value: Shared<T>) -> T {
shared_try_take(value)
.ok()
.unwrap_or_else(|| panic!("`value` is shared (i.e. has outstanding references)"))
}
#[inline(always)]
#[must_use]
#[allow(dead_code)]
pub fn locked_read<T>(value: &Locked<T>) -> Option<LockGuard<'_, T>> {
#[cfg(not(feature = "sync"))]
return value.try_borrow().ok();
#[cfg(feature = "sync")]
#[cfg(not(feature = "no_std"))]
{
#[cfg(feature = "unchecked")]
return value.read().ok();
#[cfg(not(feature = "unchecked"))]
{
for _ in 0..5 {
match value.try_read() {
Ok(guard) => return Some(guard),
Err(std::sync::TryLockError::WouldBlock) => {
std::thread::sleep(std::time::Duration::from_millis(10))
}
Err(_) => return None,
}
}
return None;
}
}
#[cfg(feature = "sync")]
#[cfg(feature = "no_std")]
{
#[cfg(feature = "unchecked")]
return Some(value.read());
#[cfg(not(feature = "unchecked"))]
return value.try_read();
}
}
#[inline(always)]
#[must_use]
#[allow(dead_code)]
pub fn locked_write<T>(value: &Locked<T>) -> Option<LockGuardMut<'_, T>> {
#[cfg(not(feature = "sync"))]
return value.try_borrow_mut().ok();
#[cfg(feature = "sync")]
#[cfg(not(feature = "no_std"))]
{
#[cfg(feature = "unchecked")]
return value.write().ok();
#[cfg(not(feature = "unchecked"))]
{
for _ in 0..5 {
match value.try_write() {
Ok(guard) => return Some(guard),
Err(std::sync::TryLockError::WouldBlock) => {
std::thread::sleep(std::time::Duration::from_millis(10))
}
Err(_) => return None,
}
}
return None;
}
}
#[cfg(feature = "sync")]
#[cfg(feature = "no_std")]
{
#[cfg(feature = "unchecked")]
return Some(value.write());
#[cfg(not(feature = "unchecked"))]
return value.try_write();
}
}
#[cfg(not(feature = "sync"))]
pub type FnAny = dyn Fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult;
#[cfg(feature = "sync")]
pub type FnAny = dyn Fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult + Send + Sync;
pub type FnBuiltin = (
fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult,
bool,
);
#[cfg(not(feature = "sync"))]
pub type FnIterator = dyn Fn(Dynamic) -> Box<dyn Iterator<Item = RhaiResultOf<Dynamic>>>;
#[cfg(feature = "sync")]
pub type FnIterator =
dyn Fn(Dynamic) -> Box<dyn Iterator<Item = RhaiResultOf<Dynamic>>> + Send + Sync;
#[cfg(not(feature = "sync"))]
pub type FnPlugin = dyn PluginFunc;
#[cfg(feature = "sync")]
pub type FnPlugin = dyn PluginFunc + Send + Sync;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "sync"))]
pub type OnProgressCallback = dyn Fn(u64) -> Option<Dynamic>;
#[cfg(not(feature = "unchecked"))]
#[cfg(feature = "sync")]
pub type OnProgressCallback = dyn Fn(u64) -> Option<Dynamic> + Send + Sync;
#[cfg(not(feature = "sync"))]
pub type OnPrintCallback = dyn Fn(&str);
#[cfg(feature = "sync")]
pub type OnPrintCallback = dyn Fn(&str) + Send + Sync;
#[cfg(not(feature = "sync"))]
pub type OnDebugCallback = dyn Fn(&str, Option<&str>, Position);
#[cfg(feature = "sync")]
pub type OnDebugCallback = dyn Fn(&str, Option<&str>, Position) + Send + Sync;
#[cfg(not(feature = "sync"))]
#[cfg(not(feature = "no_index"))]
#[cfg(feature = "internals")]
pub type OnInvalidArrayIndexCallback = dyn for<'a> Fn(
&'a mut crate::Array,
crate::INT,
EvalContext,
) -> RhaiResultOf<crate::Target<'a>>;
#[cfg(feature = "sync")]
#[cfg(not(feature = "no_index"))]
#[cfg(feature = "internals")]
pub type OnInvalidArrayIndexCallback = dyn for<'a> Fn(&'a mut crate::Array, crate::INT, EvalContext) -> RhaiResultOf<crate::Target<'a>>
+ Send
+ Sync;
#[cfg(not(feature = "sync"))]
#[cfg(not(feature = "no_object"))]
#[cfg(feature = "internals")]
pub type OnMissingMapPropertyCallback =
dyn for<'a> Fn(&'a mut crate::Map, &str, EvalContext) -> RhaiResultOf<crate::eval::Target<'a>>;
#[cfg(feature = "sync")]
#[cfg(not(feature = "no_object"))]
#[cfg(feature = "internals")]
pub type OnMissingMapPropertyCallback = dyn for<'a> Fn(&'a mut crate::Map, &str, EvalContext) -> RhaiResultOf<crate::eval::Target<'a>>
+ Send
+ Sync;
#[cfg(not(feature = "sync"))]
pub type OnParseTokenCallback = dyn Fn(Token, Position, &TokenizeState) -> Token;
#[cfg(feature = "sync")]
pub type OnParseTokenCallback = dyn Fn(Token, Position, &TokenizeState) -> Token + Send + Sync;
#[cfg(not(feature = "sync"))]
pub type OnVarCallback = dyn Fn(&str, usize, EvalContext) -> RhaiResultOf<Option<Dynamic>>;
#[cfg(feature = "sync")]
pub type OnVarCallback =
dyn Fn(&str, usize, EvalContext) -> RhaiResultOf<Option<Dynamic>> + Send + Sync;
#[cfg(not(feature = "sync"))]
pub type OnDefVarCallback = dyn Fn(bool, VarDefInfo, EvalContext) -> RhaiResultOf<bool>;
#[cfg(feature = "sync")]
pub type OnDefVarCallback =
dyn Fn(bool, VarDefInfo, EvalContext) -> RhaiResultOf<bool> + Send + Sync;