1use super::call::FnCallArgs;
4use crate::ast::FnCallHashes;
5use crate::eval::{Caches, GlobalRuntimeState};
6use crate::plugin::PluginFunc;
7use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
8use crate::types::dynamic::Variant;
9use crate::{
10 calc_fn_hash, expose_under_internals, Dynamic, Engine, EvalContext, FnArgsVec, FuncArgs,
11 Position, RhaiResult, RhaiResultOf, StaticVec, VarDefInfo, ERR,
12};
13use std::any::type_name;
14#[cfg(feature = "no_std")]
15use std::prelude::v1::*;
16
17#[cfg(feature = "sync")]
19pub trait SendSync: Send + Sync {}
20#[cfg(feature = "sync")]
22impl<T: Send + Sync> SendSync for T {}
23
24#[cfg(not(feature = "sync"))]
26pub trait SendSync {}
27#[cfg(not(feature = "sync"))]
29impl<T> SendSync for T {}
30
31#[cfg(not(feature = "sync"))]
33pub use alloc::rc::Rc as Shared;
38#[cfg(feature = "sync")]
40pub use alloc::sync::Arc as Shared;
44
45#[cfg(not(feature = "sync"))]
47pub use std::cell::RefCell as Locked;
48
49#[cfg(not(feature = "sync"))]
51pub type LockGuard<'a, T> = std::cell::Ref<'a, T>;
52
53#[cfg(not(feature = "sync"))]
55pub type LockGuardMut<'a, T> = std::cell::RefMut<'a, T>;
56
57#[cfg(feature = "sync")]
59#[allow(dead_code)]
60pub use std::sync::RwLock as Locked;
61
62#[cfg(feature = "sync")]
64#[allow(dead_code)]
65pub type LockGuard<'a, T> = std::sync::RwLockReadGuard<'a, T>;
66
67#[cfg(feature = "sync")]
69#[allow(dead_code)]
70pub type LockGuardMut<'a, T> = std::sync::RwLockWriteGuard<'a, T>;
71
72#[derive(Debug)]
74pub struct NativeCallContext<'a> {
75 engine: &'a Engine,
77 fn_name: &'a str,
79 source: Option<&'a str>,
81 global: &'a GlobalRuntimeState,
83 pos: Position,
85}
86
87#[deprecated = "This type is NOT deprecated, but it is considered volatile and may change in the future."]
94#[cfg(feature = "internals")]
95#[derive(Debug, Clone)]
96pub struct NativeCallContextStore {
97 pub fn_name: String,
99 pub source: Option<String>,
101 pub global: GlobalRuntimeState,
103 pub pos: Position,
105}
106
107#[cfg(feature = "internals")]
108#[allow(deprecated)]
109impl NativeCallContextStore {
110 #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
116 #[inline(always)]
117 #[must_use]
118 pub fn create_context<'a>(&'a self, engine: &'a Engine) -> NativeCallContext<'a> {
119 NativeCallContext::from_stored_data(engine, self)
120 }
121}
122
123impl<'a>
124 From<(
125 &'a Engine,
126 &'a str,
127 Option<&'a str>,
128 &'a GlobalRuntimeState,
129 Position,
130 )> for NativeCallContext<'a>
131{
132 #[inline(always)]
133 fn from(
134 value: (
135 &'a Engine,
136 &'a str,
137 Option<&'a str>,
138 &'a GlobalRuntimeState,
139 Position,
140 ),
141 ) -> Self {
142 Self {
143 engine: value.0,
144 fn_name: value.1,
145 source: value.2,
146 global: value.3,
147 pos: value.4,
148 }
149 }
150}
151
152impl<'a> NativeCallContext<'a> {
153 #[cfg(feature = "internals")]
158 #[cfg(not(feature = "no_module"))]
159 #[inline(always)]
160 #[must_use]
161 pub const fn new_with_all_fields(
162 engine: &'a Engine,
163 fn_name: &'a str,
164 source: Option<&'a str>,
165 global: &'a GlobalRuntimeState,
166 pos: Position,
167 ) -> Self {
168 Self {
169 engine,
170 fn_name,
171 source,
172 global,
173 pos,
174 }
175 }
176
177 #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
184 #[cfg(feature = "internals")]
185 #[inline]
186 #[must_use]
187 #[allow(deprecated)]
188 pub fn from_stored_data(engine: &'a Engine, context: &'a NativeCallContextStore) -> Self {
189 Self {
190 engine,
191 fn_name: &context.fn_name,
192 source: context.source.as_deref(),
193 global: &context.global,
194 pos: context.pos,
195 }
196 }
197 #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
204 #[cfg(feature = "internals")]
205 #[inline]
206 #[must_use]
207 #[allow(deprecated)]
208 pub fn store_data(&self) -> NativeCallContextStore {
209 NativeCallContextStore {
210 fn_name: self.fn_name.to_string(),
211 source: self.source.map(ToString::to_string),
212 global: self.global.clone(),
213 pos: self.pos,
214 }
215 }
216
217 #[inline(always)]
219 #[must_use]
220 pub const fn engine(&self) -> &Engine {
221 self.engine
222 }
223 #[inline(always)]
225 #[must_use]
226 pub const fn fn_name(&self) -> &str {
227 self.fn_name
228 }
229 #[inline(always)]
231 #[must_use]
232 pub const fn fn_source(&self) -> Option<&str> {
233 self.source
234 }
235 #[inline(always)]
237 #[must_use]
238 pub fn call_source(&self) -> Option<&str> {
239 self.global.source.as_deref()
240 }
241 #[inline(always)]
243 #[must_use]
244 pub const fn call_position(&self) -> Position {
245 self.pos
246 }
247 #[inline(always)]
249 #[must_use]
250 pub const fn call_level(&self) -> usize {
251 self.global.level
252 }
253 #[inline(always)]
255 #[must_use]
256 pub const fn tag(&self) -> Option<&Dynamic> {
257 Some(&self.global.tag)
258 }
259 #[cfg(not(feature = "no_module"))]
264 #[inline]
265 pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
266 self.global.iter_imports()
267 }
268 #[expose_under_internals]
273 #[inline(always)]
274 #[must_use]
275 const fn global_runtime_state(&self) -> &GlobalRuntimeState {
276 self.global
277 }
278 #[cfg(not(feature = "no_function"))]
283 #[inline]
284 pub fn iter_namespaces(&self) -> impl Iterator<Item = &crate::Module> {
285 self.global.lib.iter().map(<_>::as_ref)
286 }
287 #[cfg(not(feature = "no_function"))]
292 #[cfg(feature = "internals")]
293 #[inline(always)]
294 #[must_use]
295 pub fn namespaces(&self) -> &[crate::SharedModule] {
296 &self.global.lib
297 }
298 #[inline]
300 pub fn call_fn<T: Variant + Clone>(
301 &self,
302 fn_name: impl AsRef<str>,
303 args: impl FuncArgs,
304 ) -> RhaiResultOf<T> {
305 let mut arg_values = StaticVec::new_const();
306 args.parse(&mut arg_values);
307
308 let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();
309
310 self._call_fn_raw(fn_name, args, false, false, false)
311 .and_then(|result| {
312 result.try_cast_result().map_err(|r| {
313 let result_type = self.engine().map_type_name(r.type_name());
314 let cast_type = match type_name::<T>() {
315 typ if typ.contains("::") => self.engine.map_type_name(typ),
316 typ => typ,
317 };
318 ERR::ErrorMismatchOutputType(
319 cast_type.into(),
320 result_type.into(),
321 self.call_position(),
322 )
323 .into()
324 })
325 })
326 }
327 #[inline]
333 pub fn call_native_fn<T: Variant + Clone>(
334 &self,
335 fn_name: impl AsRef<str>,
336 args: impl FuncArgs,
337 ) -> RhaiResultOf<T> {
338 let mut arg_values = StaticVec::new_const();
339 args.parse(&mut arg_values);
340
341 let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();
342
343 self._call_fn_raw(fn_name, args, true, false, false)
344 .and_then(|result| {
345 result.try_cast_result().map_err(|r| {
346 let result_type = self.engine().map_type_name(r.type_name());
347 let cast_type = match type_name::<T>() {
348 typ if typ.contains("::") => self.engine.map_type_name(typ),
349 typ => typ,
350 };
351 ERR::ErrorMismatchOutputType(
352 cast_type.into(),
353 result_type.into(),
354 self.call_position(),
355 )
356 .into()
357 })
358 })
359 }
360 #[inline(always)]
380 pub fn call_fn_raw(
381 &self,
382 fn_name: impl AsRef<str>,
383 is_ref_mut: bool,
384 is_method_call: bool,
385 args: &mut [&mut Dynamic],
386 ) -> RhaiResult {
387 let name = fn_name.as_ref();
388 let native_only = !is_valid_function_name(name);
389 #[cfg(not(feature = "no_function"))]
390 let native_only = native_only && !crate::parser::is_anonymous_fn(name);
391
392 self._call_fn_raw(fn_name, args, native_only, is_ref_mut, is_method_call)
393 }
394 #[inline(always)]
415 pub fn call_native_fn_raw(
416 &self,
417 fn_name: impl AsRef<str>,
418 is_ref_mut: bool,
419 args: &mut [&mut Dynamic],
420 ) -> RhaiResult {
421 self._call_fn_raw(fn_name, args, true, is_ref_mut, false)
422 }
423
424 fn _call_fn_raw(
426 &self,
427 fn_name: impl AsRef<str>,
428 args: &mut [&mut Dynamic],
429 native_only: bool,
430 is_ref_mut: bool,
431 is_method_call: bool,
432 ) -> RhaiResult {
433 let global = &mut self.global.clone();
434 global.level += 1;
435
436 let caches = &mut Caches::new();
437
438 let fn_name = fn_name.as_ref();
439 let op_token = Token::lookup_symbol_from_syntax(fn_name);
440 let args_len = args.len();
441
442 if native_only {
443 return self
444 .engine()
445 .exec_native_fn_call(
446 global,
447 caches,
448 fn_name,
449 op_token.as_ref(),
450 calc_fn_hash(None, fn_name, args_len),
451 args,
452 is_ref_mut,
453 false,
454 self.call_position(),
455 )
456 .map(|(r, ..)| r);
457 }
458
459 let hash = match is_method_call {
462 #[cfg(not(feature = "no_function"))]
463 true => FnCallHashes::from_script_and_native(
464 calc_fn_hash(None, fn_name, args_len - 1),
465 calc_fn_hash(None, fn_name, args_len),
466 ),
467 #[cfg(feature = "no_function")]
468 true => FnCallHashes::from_native_only(calc_fn_hash(None, fn_name, args_len)),
469 _ => FnCallHashes::from_hash(calc_fn_hash(None, fn_name, args_len)),
470 };
471
472 self.engine()
473 .exec_fn_call(
474 global,
475 caches,
476 None,
477 fn_name,
478 op_token.as_ref(),
479 hash,
480 args,
481 is_ref_mut,
482 is_method_call,
483 self.call_position(),
484 )
485 .map(|(r, ..)| r)
486 }
487}
488
489#[inline(always)]
492#[must_use]
493#[allow(dead_code)]
494pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
495 Shared::make_mut(value)
496}
497
498#[inline(always)]
500#[must_use]
501#[allow(dead_code)]
502pub fn shared_get_mut<T: Clone>(value: &mut Shared<T>) -> Option<&mut T> {
503 Shared::get_mut(value)
504}
505
506#[inline]
508#[must_use]
509#[allow(dead_code)]
510pub fn shared_take_or_clone<T: Clone>(value: Shared<T>) -> T {
511 shared_try_take(value).unwrap_or_else(|v| v.as_ref().clone())
512}
513
514#[inline(always)]
516#[allow(dead_code)]
517pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
518 Shared::try_unwrap(value)
519}
520
521#[inline]
527#[must_use]
528#[allow(dead_code)]
529pub fn shared_take<T>(value: Shared<T>) -> T {
530 shared_try_take(value)
531 .ok()
532 .unwrap_or_else(|| panic!("`value` is shared (i.e. has outstanding references)"))
533}
534
535#[inline(always)]
538#[must_use]
539#[allow(dead_code)]
540pub fn locked_read<T>(value: &Locked<T>) -> Option<LockGuard<'_, T>> {
541 #[cfg(not(feature = "sync"))]
542 return value.try_borrow().ok();
543
544 #[cfg(feature = "sync")]
545 #[cfg(not(feature = "no_std"))]
546 {
547 #[cfg(feature = "unchecked")]
548 return value.read().ok();
549
550 #[cfg(not(feature = "unchecked"))]
551 {
552 for _ in 0..5 {
554 match value.try_read() {
555 Ok(guard) => return Some(guard),
556 Err(std::sync::TryLockError::WouldBlock) => {
557 std::thread::sleep(std::time::Duration::from_millis(10))
558 }
559 Err(_) => return None,
560 }
561 }
562
563 return None;
564 }
565 }
566
567 #[cfg(feature = "sync")]
568 #[cfg(feature = "no_std")]
569 {
570 #[cfg(feature = "unchecked")]
571 return Some(value.read());
572 #[cfg(not(feature = "unchecked"))]
573 return value.try_read();
574 }
575}
576
577#[inline(always)]
580#[must_use]
581#[allow(dead_code)]
582pub fn locked_write<T>(value: &Locked<T>) -> Option<LockGuardMut<'_, T>> {
583 #[cfg(not(feature = "sync"))]
584 return value.try_borrow_mut().ok();
585
586 #[cfg(feature = "sync")]
587 #[cfg(not(feature = "no_std"))]
588 {
589 #[cfg(feature = "unchecked")]
590 return value.write().ok();
591
592 #[cfg(not(feature = "unchecked"))]
593 {
594 for _ in 0..5 {
596 match value.try_write() {
597 Ok(guard) => return Some(guard),
598 Err(std::sync::TryLockError::WouldBlock) => {
599 std::thread::sleep(std::time::Duration::from_millis(10))
600 }
601 Err(_) => return None,
602 }
603 }
604
605 return None;
606 }
607 }
608
609 #[cfg(feature = "sync")]
610 #[cfg(feature = "no_std")]
611 {
612 #[cfg(feature = "unchecked")]
613 return Some(value.write());
614 #[cfg(not(feature = "unchecked"))]
615 return value.try_write();
616 }
617}
618
619#[cfg(not(feature = "sync"))]
621pub type FnAny = dyn Fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult;
622#[cfg(feature = "sync")]
624pub type FnAny = dyn Fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult + Send + Sync;
625
626pub type FnBuiltin = (
628 fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult,
629 bool,
630);
631
632#[cfg(not(feature = "sync"))]
634pub type FnIterator = dyn Fn(Dynamic) -> Box<dyn Iterator<Item = RhaiResultOf<Dynamic>>>;
635#[cfg(feature = "sync")]
637pub type FnIterator =
638 dyn Fn(Dynamic) -> Box<dyn Iterator<Item = RhaiResultOf<Dynamic>>> + Send + Sync;
639
640#[cfg(not(feature = "sync"))]
642pub type FnPlugin = dyn PluginFunc;
643#[cfg(feature = "sync")]
645pub type FnPlugin = dyn PluginFunc + Send + Sync;
646
647#[cfg(not(feature = "unchecked"))]
649#[cfg(not(feature = "sync"))]
650pub type OnProgressCallback = dyn Fn(u64) -> Option<Dynamic>;
651#[cfg(not(feature = "unchecked"))]
653#[cfg(feature = "sync")]
654pub type OnProgressCallback = dyn Fn(u64) -> Option<Dynamic> + Send + Sync;
655
656#[cfg(not(feature = "sync"))]
658pub type OnPrintCallback = dyn Fn(&str);
659#[cfg(feature = "sync")]
661pub type OnPrintCallback = dyn Fn(&str) + Send + Sync;
662
663#[cfg(not(feature = "sync"))]
665pub type OnDebugCallback = dyn Fn(&str, Option<&str>, Position);
666#[cfg(feature = "sync")]
668pub type OnDebugCallback = dyn Fn(&str, Option<&str>, Position) + Send + Sync;
669
670#[cfg(not(feature = "sync"))]
673#[cfg(not(feature = "no_index"))]
674#[cfg(feature = "internals")]
675pub type OnInvalidArrayIndexCallback = dyn for<'a> Fn(
676 &'a mut crate::Array,
677 crate::INT,
678 EvalContext,
679) -> RhaiResultOf<crate::Target<'a>>;
680#[cfg(feature = "sync")]
683#[cfg(not(feature = "no_index"))]
684#[cfg(feature = "internals")]
685pub type OnInvalidArrayIndexCallback = dyn for<'a> Fn(&'a mut crate::Array, crate::INT, EvalContext) -> RhaiResultOf<crate::Target<'a>>
686 + Send
687 + Sync;
688
689#[cfg(not(feature = "sync"))]
692#[cfg(not(feature = "no_object"))]
693#[cfg(feature = "internals")]
694pub type OnMissingMapPropertyCallback =
695 dyn for<'a> Fn(&'a mut crate::Map, &str, EvalContext) -> RhaiResultOf<crate::eval::Target<'a>>;
696#[cfg(feature = "sync")]
699#[cfg(not(feature = "no_object"))]
700#[cfg(feature = "internals")]
701pub type OnMissingMapPropertyCallback = dyn for<'a> Fn(&'a mut crate::Map, &str, EvalContext) -> RhaiResultOf<crate::eval::Target<'a>>
702 + Send
703 + Sync;
704
705#[cfg(not(feature = "sync"))]
707pub type OnParseTokenCallback = dyn Fn(Token, Position, &TokenizeState) -> Token;
708#[cfg(feature = "sync")]
710pub type OnParseTokenCallback = dyn Fn(Token, Position, &TokenizeState) -> Token + Send + Sync;
711
712#[cfg(not(feature = "sync"))]
714pub type OnVarCallback = dyn Fn(&str, usize, EvalContext) -> RhaiResultOf<Option<Dynamic>>;
715#[cfg(feature = "sync")]
717pub type OnVarCallback =
718 dyn Fn(&str, usize, EvalContext) -> RhaiResultOf<Option<Dynamic>> + Send + Sync;
719
720#[cfg(not(feature = "sync"))]
722pub type OnDefVarCallback = dyn Fn(bool, VarDefInfo, EvalContext) -> RhaiResultOf<bool>;
723#[cfg(feature = "sync")]
725pub type OnDefVarCallback =
726 dyn Fn(bool, VarDefInfo, EvalContext) -> RhaiResultOf<bool> + Send + Sync;