1use crate::func::FnCallArgs;
4use crate::tokenizer::{is_reserved_keyword_or_symbol, is_valid_function_name, Token};
5use crate::types::dynamic::Variant;
6use crate::{
7 expose_under_internals, Dynamic, Engine, FnArgsVec, FuncArgs, ImmutableString,
8 NativeCallContext, Position, RhaiError, RhaiResult, RhaiResultOf, Shared, StaticVec, ThinVec,
9 AST, ERR, PERR,
10};
11#[cfg(feature = "no_std")]
12use std::prelude::v1::*;
13use std::{
14 any::type_name,
15 convert::{TryFrom, TryInto},
16 fmt, mem,
17 ops::{Index, IndexMut},
18};
19
20#[derive(Clone, Default)]
22pub enum FnPtrType {
23 #[default]
25 Normal,
26 #[cfg(not(feature = "no_function"))]
28 Script(Shared<crate::ast::ScriptFuncDef>),
29 #[cfg(not(feature = "sync"))]
31 Native(Shared<dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + 'static>),
32 #[cfg(feature = "sync")]
33 Native(
34 Shared<dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync + 'static>,
35 ),
36}
37
38impl fmt::Display for FnPtrType {
39 #[cold]
40 #[inline(never)]
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match self {
43 Self::Normal => f.write_str("Fn"),
44 #[cfg(not(feature = "no_function"))]
45 Self::Script(..) => f.write_str("Fn*"),
46 Self::Native(..) => f.write_str("Fn"),
47 }
48 }
49}
50
51#[derive(Clone)]
54pub struct FnPtr {
55 pub(crate) name: ImmutableString,
57 pub(crate) curry: ThinVec<Dynamic>,
59 #[cfg(not(feature = "no_function"))]
61 pub(crate) env: Option<Shared<crate::ast::EncapsulatedEnviron>>,
62 pub(crate) typ: FnPtrType,
64}
65
66impl fmt::Display for FnPtr {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 write!(f, "Fn({})", self.fn_name())
69 }
70}
71
72impl fmt::Debug for FnPtr {
73 #[cold]
74 #[inline(never)]
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 let name = match self {
77 #[cfg(not(feature = "no_function"))]
78 _ if self.env.is_some() => format!("{}+", self.typ),
79 _ => self.typ.to_string(),
80 };
81 let ff = &mut f.debug_tuple(&name);
82 ff.field(&self.name);
83 self.curry.iter().for_each(|curry| {
84 ff.field(curry);
85 });
86 ff.finish()?;
87
88 Ok(())
89 }
90}
91
92impl FnPtr {
93 #[inline(always)]
99 pub fn new(name: impl Into<ImmutableString>) -> RhaiResultOf<Self> {
100 name.into().try_into()
101 }
102 #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
116 #[inline(always)]
117 pub fn from_fn(
118 name: impl Into<ImmutableString>,
119 #[cfg(not(feature = "sync"))] func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult
120 + 'static,
121 #[cfg(feature = "sync")] func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult
122 + Send
123 + Sync
124 + 'static,
125 ) -> RhaiResultOf<Self> {
126 #[allow(deprecated)]
127 Self::from_dyn_fn(name, Box::new(func))
128 }
129 #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
143 #[inline]
144 pub fn from_dyn_fn(
145 name: impl Into<ImmutableString>,
146 #[cfg(not(feature = "sync"))] func: Box<
147 dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + 'static,
148 >,
149 #[cfg(feature = "sync")] func: Box<
150 dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync + 'static,
151 >,
152 ) -> RhaiResultOf<Self> {
153 let mut fp = Self::new(name)?;
154 fp.typ = FnPtrType::Native(Shared::new(func));
155 Ok(fp)
156 }
157
158 #[inline(always)]
160 #[must_use]
161 pub fn fn_name(&self) -> &str {
162 self.fn_name_raw()
163 }
164 #[inline(always)]
166 #[must_use]
167 pub(crate) const fn fn_name_raw(&self) -> &ImmutableString {
168 &self.name
169 }
170 #[inline(always)]
172 pub fn curry(&self) -> &[Dynamic] {
173 self.curry.as_ref()
174 }
175 #[inline(always)]
177 pub fn iter_curry(&self) -> impl Iterator<Item = &Dynamic> {
178 self.curry.iter()
179 }
180 #[inline(always)]
182 pub fn iter_curry_mut(&mut self) -> impl Iterator<Item = &mut Dynamic> {
183 self.curry.iter_mut()
184 }
185 #[inline(always)]
187 pub fn add_curry(&mut self, value: Dynamic) -> &mut Self {
188 self.curry.push(value);
189 self
190 }
191 #[inline]
193 pub fn set_curry(&mut self, values: impl IntoIterator<Item = Dynamic>) -> &mut Self {
194 self.curry = values.into_iter().collect();
195 self
196 }
197 #[inline(always)]
199 #[must_use]
200 pub fn is_curried(&self) -> bool {
201 !self.curry.is_empty()
202 }
203 #[cfg(not(feature = "no_function"))]
207 #[inline(always)]
208 #[must_use]
209 pub fn is_anonymous(&self) -> bool {
210 crate::func::is_anonymous_fn(&self.name)
211 }
212 #[inline]
244 pub fn call<T: Variant + Clone>(
245 &self,
246 engine: &Engine,
247 ast: &AST,
248 args: impl FuncArgs,
249 ) -> RhaiResultOf<T> {
250 let _ast = ast;
251 let mut arg_values = StaticVec::new_const();
252 args.parse(&mut arg_values);
253
254 let global = &mut engine.new_global_runtime_state();
255
256 #[cfg(not(feature = "no_function"))]
257 global.lib.push(_ast.shared_lib().clone());
258
259 let ctx = (engine, self.fn_name(), None, &*global, Position::NONE).into();
260
261 self.call_raw(&ctx, None, arg_values).and_then(|result| {
262 result.try_cast_result().map_err(|r| {
263 let result_type = engine.map_type_name(r.type_name());
264 let cast_type = match type_name::<T>() {
265 typ if typ.contains("::") => engine.map_type_name(typ),
266 typ => typ,
267 };
268 ERR::ErrorMismatchOutputType(cast_type.into(), result_type.into(), Position::NONE)
269 .into()
270 })
271 })
272 }
273 #[cfg(not(feature = "no_object"))]
281 #[inline]
282 pub fn call_as_method<T: Variant + Clone>(
283 &self,
284 engine: &Engine,
285 ast: &AST,
286 this_ptr: &mut Dynamic,
287 args: impl FuncArgs,
288 ) -> RhaiResultOf<T> {
289 let _ast = ast;
290 let mut arg_values = StaticVec::new_const();
291 args.parse(&mut arg_values);
292
293 let global = &mut engine.new_global_runtime_state();
294
295 #[cfg(not(feature = "no_function"))]
296 global.lib.push(_ast.shared_lib().clone());
297
298 let ctx = (engine, self.fn_name(), None, &*global, Position::NONE).into();
299
300 self.call_raw(&ctx, Some(this_ptr), arg_values)
301 .and_then(|result| {
302 result.try_cast_result().map_err(|r| {
303 let result_type = engine.map_type_name(r.type_name());
304 let cast_type = match type_name::<T>() {
305 typ if typ.contains("::") => engine.map_type_name(typ),
306 typ => typ,
307 };
308 ERR::ErrorMismatchOutputType(
309 cast_type.into(),
310 result_type.into(),
311 Position::NONE,
312 )
313 .into()
314 })
315 })
316 }
317 #[inline]
324 pub fn call_within_context<T: Variant + Clone>(
325 &self,
326 context: &NativeCallContext,
327 args: impl FuncArgs,
328 ) -> RhaiResultOf<T> {
329 let mut arg_values = StaticVec::new_const();
330 args.parse(&mut arg_values);
331
332 self.call_raw(context, None, arg_values).and_then(|result| {
333 result.try_cast_result().map_err(|r| {
334 let result_type = context.engine().map_type_name(r.type_name());
335 let cast_type = match type_name::<T>() {
336 typ if typ.contains("::") => context.engine().map_type_name(typ),
337 typ => typ,
338 };
339 ERR::ErrorMismatchOutputType(cast_type.into(), result_type.into(), Position::NONE)
340 .into()
341 })
342 })
343 }
344 #[cfg(not(feature = "no_object"))]
353 #[inline]
354 pub fn call_as_method_within_context<T: Variant + Clone>(
355 &self,
356 context: &NativeCallContext,
357 this_ptr: &mut Dynamic,
358 args: impl FuncArgs,
359 ) -> RhaiResultOf<T> {
360 let mut arg_values = StaticVec::new_const();
361 args.parse(&mut arg_values);
362
363 self.call_raw(context, Some(this_ptr), arg_values)
364 .and_then(|result| {
365 result.try_cast_result().map_err(|r| {
366 let result_type = context.engine().map_type_name(r.type_name());
367 let cast_type = match type_name::<T>() {
368 typ if typ.contains("::") => context.engine().map_type_name(typ),
369 typ => typ,
370 };
371 ERR::ErrorMismatchOutputType(
372 cast_type.into(),
373 result_type.into(),
374 Position::NONE,
375 )
376 .into()
377 })
378 })
379 }
380 #[inline]
399 pub fn call_raw(
400 &self,
401 context: &NativeCallContext,
402 this_ptr: Option<&mut Dynamic>,
403 arg_values: impl AsMut<[Dynamic]>,
404 ) -> RhaiResult {
405 let mut arg_values = arg_values;
406 let mut arg_values = arg_values.as_mut();
407 let mut args_data;
408
409 if self.is_curried() {
410 args_data = FnArgsVec::with_capacity(self.curry().len() + arg_values.len());
411 args_data.extend(self.curry().iter().cloned());
412 args_data.extend(arg_values.iter_mut().map(mem::take));
413 arg_values = &mut *args_data;
414 }
415
416 let args = &mut StaticVec::with_capacity(arg_values.len() + 1);
417 args.extend(arg_values.iter_mut());
418
419 match self.typ {
420 #[cfg(not(feature = "no_function"))]
422 FnPtrType::Script(ref fn_def) if fn_def.params.len() == args.len() => {
423 let global = &mut context.global_runtime_state().clone();
424 global.level += 1;
425
426 let caches = &mut crate::eval::Caches::new();
427
428 return context.engine().call_script_fn(
429 global,
430 caches,
431 &mut crate::Scope::new(),
432 this_ptr,
433 #[cfg(not(feature = "no_function"))]
434 self.env.as_deref(),
435 #[cfg(feature = "no_function")]
436 None,
437 fn_def,
438 args,
439 true,
440 context.call_position(),
441 );
442 }
443 _ => (),
444 }
445
446 let is_method = this_ptr.is_some();
447
448 if let Some(obj) = this_ptr {
449 args.insert(0, obj);
450 }
451
452 context.call_fn_raw(self.fn_name(), is_method, is_method, args)
453 }
454
455 #[expose_under_internals]
470 #[inline(always)]
471 fn call_raw_with_extra_args<const N: usize, const E: usize>(
472 &self,
473 caller_fn: &str,
474 ctx: &NativeCallContext,
475 this_ptr: Option<&mut Dynamic>,
476 args: [Dynamic; N],
477 extras: [Dynamic; E],
478 move_this_ptr_to_args: Option<usize>,
479 ) -> RhaiResult {
480 match move_this_ptr_to_args {
481 Some(m) => {
482 self._call_with_extra_args::<true, N, E>(caller_fn, ctx, this_ptr, args, extras, m)
483 }
484 None => {
485 self._call_with_extra_args::<false, N, E>(caller_fn, ctx, this_ptr, args, extras, 0)
486 }
487 }
488 }
489 fn _call_with_extra_args<const MOVE_PTR: bool, const N: usize, const E: usize>(
492 &self,
493 caller_fn: &str,
494 ctx: &NativeCallContext,
495 mut this_ptr: Option<&mut Dynamic>,
496 args: [Dynamic; N],
497 extras: [Dynamic; E],
498 move_this_ptr_to_args: usize,
499 ) -> RhaiResult {
500 match self.typ {
501 #[cfg(not(feature = "no_function"))]
502 FnPtrType::Script(ref fn_def) => {
503 let arity = fn_def.params.len();
504
505 if arity == N + self.curry().len() {
506 return self.call_raw(ctx, this_ptr, args);
507 }
508 if MOVE_PTR {
509 if let Some(this_ptr) = this_ptr.as_deref() {
510 if arity == N + 1 + self.curry().len() {
511 let mut args2 = FnArgsVec::with_capacity(args.len() + 1);
512 if move_this_ptr_to_args == 0 {
513 args2.push(this_ptr.clone());
514 args2.extend(args);
515 } else {
516 args2.extend(args);
517 args2.insert(move_this_ptr_to_args, this_ptr.clone());
518 }
519 return self.call_raw(ctx, None, args2);
520 }
521 if arity == N + E + 1 + self.curry().len() {
522 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1);
523 if move_this_ptr_to_args == 0 {
524 args2.push(this_ptr.clone());
525 args2.extend(args);
526 } else {
527 args2.extend(args);
528 args2.insert(move_this_ptr_to_args, this_ptr.clone());
529 }
530 args2.extend(extras);
531 return self.call_raw(ctx, None, args2);
532 }
533 }
534 }
535 if arity == N + E + self.curry().len() {
536 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len());
537 args2.extend(args);
538 args2.extend(extras);
539 return self.call_raw(ctx, this_ptr, args2);
540 }
541 }
542 _ => (),
543 }
544
545 self.call_raw(ctx, this_ptr.as_deref_mut(), args.clone())
546 .or_else(|err| match *err {
547 ERR::ErrorFunctionNotFound(sig, ..)
548 if MOVE_PTR && this_ptr.is_some() && sig.starts_with(self.fn_name()) =>
549 {
550 let mut args2 = FnArgsVec::with_capacity(args.len() + 1);
551 if move_this_ptr_to_args == 0 {
552 args2.push(this_ptr.as_mut().unwrap().clone());
553 args2.extend(args.clone());
554 } else {
555 args2.extend(args.clone());
556 args2.insert(move_this_ptr_to_args, this_ptr.as_mut().unwrap().clone());
557 }
558 self.call_raw(ctx, None, args2)
559 }
560 _ => Err(err),
561 })
562 .or_else(|err| match *err {
563 ERR::ErrorFunctionNotFound(sig, ..) if sig.starts_with(self.fn_name()) => {
564 if MOVE_PTR {
565 if let Some(ref mut this_ptr) = this_ptr {
566 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1);
567 if move_this_ptr_to_args == 0 {
568 args2.push(this_ptr.clone());
569 args2.extend(args);
570 args2.extend(extras);
571 } else {
572 args2.extend(args);
573 args2.extend(extras);
574 args2.insert(move_this_ptr_to_args, this_ptr.clone());
575 }
576 return self.call_raw(ctx, None, args2);
577 }
578 }
579
580 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len());
581 args2.extend(args);
582 args2.extend(extras);
583
584 self.call_raw(ctx, this_ptr, args2)
585 }
586 _ => Err(err),
587 })
588 .map_err(|err| {
589 Box::new(ERR::ErrorInFunctionCall(
590 caller_fn.to_string(),
591 ctx.call_source().unwrap_or("").to_string(),
592 err,
593 Position::NONE,
594 ))
595 })
596 }
597}
598
599impl TryFrom<ImmutableString> for FnPtr {
600 type Error = RhaiError;
601
602 #[inline(always)]
603 fn try_from(value: ImmutableString) -> RhaiResultOf<Self> {
604 if is_valid_function_name(&value) {
605 Ok(Self {
606 name: value,
607 curry: ThinVec::new(),
608 #[cfg(not(feature = "no_function"))]
609 env: None,
610 typ: FnPtrType::Normal,
611 })
612 } else if is_reserved_keyword_or_symbol(&value).0
613 || Token::lookup_symbol_from_syntax(&value).is_some()
614 {
615 Err(ERR::ErrorParsing(PERR::Reserved(value.to_string()), Position::NONE).into())
616 } else {
617 Err(ERR::ErrorFunctionNotFound(value.to_string(), Position::NONE).into())
618 }
619 }
620}
621
622#[cfg(not(feature = "no_function"))]
623impl<T: Into<Shared<crate::ast::ScriptFuncDef>>> From<T> for FnPtr {
624 #[inline(always)]
625 fn from(value: T) -> Self {
626 let fn_def = value.into();
627
628 Self {
629 name: fn_def.name.clone(),
630 curry: ThinVec::new(),
631 #[cfg(not(feature = "no_function"))]
632 env: None,
633 typ: FnPtrType::Script(fn_def),
634 }
635 }
636}
637
638impl Index<usize> for FnPtr {
639 type Output = Dynamic;
640
641 #[inline(always)]
642 fn index(&self, index: usize) -> &Self::Output {
643 self.curry.index(index)
644 }
645}
646
647impl IndexMut<usize> for FnPtr {
648 #[inline(always)]
649 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
650 self.curry.index_mut(index)
651 }
652}
653
654impl Extend<Dynamic> for FnPtr {
655 #[inline(always)]
656 fn extend<T: IntoIterator<Item = Dynamic>>(&mut self, iter: T) {
657 self.curry.extend(iter);
658 }
659}