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 #[inline]
280 pub fn call_within_context<T: Variant + Clone>(
281 &self,
282 context: &NativeCallContext,
283 args: impl FuncArgs,
284 ) -> RhaiResultOf<T> {
285 let mut arg_values = StaticVec::new_const();
286 args.parse(&mut arg_values);
287
288 self.call_raw(context, None, arg_values).and_then(|result| {
289 result.try_cast_result().map_err(|r| {
290 let result_type = context.engine().map_type_name(r.type_name());
291 let cast_type = match type_name::<T>() {
292 typ if typ.contains("::") => context.engine().map_type_name(typ),
293 typ => typ,
294 };
295 ERR::ErrorMismatchOutputType(cast_type.into(), result_type.into(), Position::NONE)
296 .into()
297 })
298 })
299 }
300 #[inline]
319 pub fn call_raw(
320 &self,
321 context: &NativeCallContext,
322 this_ptr: Option<&mut Dynamic>,
323 arg_values: impl AsMut<[Dynamic]>,
324 ) -> RhaiResult {
325 let mut arg_values = arg_values;
326 let mut arg_values = arg_values.as_mut();
327 let mut args_data;
328
329 if self.is_curried() {
330 args_data = FnArgsVec::with_capacity(self.curry().len() + arg_values.len());
331 args_data.extend(self.curry().iter().cloned());
332 args_data.extend(arg_values.iter_mut().map(mem::take));
333 arg_values = &mut *args_data;
334 };
335
336 let args = &mut StaticVec::with_capacity(arg_values.len() + 1);
337 args.extend(arg_values.iter_mut());
338
339 match self.typ {
340 #[cfg(not(feature = "no_function"))]
342 FnPtrType::Script(ref fn_def) if fn_def.params.len() == args.len() => {
343 let global = &mut context.global_runtime_state().clone();
344 global.level += 1;
345
346 let caches = &mut crate::eval::Caches::new();
347
348 return context.engine().call_script_fn(
349 global,
350 caches,
351 &mut crate::Scope::new(),
352 this_ptr,
353 #[cfg(not(feature = "no_function"))]
354 self.env.as_deref(),
355 #[cfg(feature = "no_function")]
356 None,
357 fn_def,
358 args,
359 true,
360 context.call_position(),
361 );
362 }
363 _ => (),
364 }
365
366 let is_method = this_ptr.is_some();
367
368 if let Some(obj) = this_ptr {
369 args.insert(0, obj);
370 }
371
372 context.call_fn_raw(self.fn_name(), is_method, is_method, args)
373 }
374
375 #[expose_under_internals]
390 #[inline(always)]
391 fn call_raw_with_extra_args<const N: usize, const E: usize>(
392 &self,
393 caller_fn: &str,
394 ctx: &NativeCallContext,
395 this_ptr: Option<&mut Dynamic>,
396 args: [Dynamic; N],
397 extras: [Dynamic; E],
398 move_this_ptr_to_args: Option<usize>,
399 ) -> RhaiResult {
400 match move_this_ptr_to_args {
401 Some(m) => {
402 self._call_with_extra_args::<true, N, E>(caller_fn, ctx, this_ptr, args, extras, m)
403 }
404 None => {
405 self._call_with_extra_args::<false, N, E>(caller_fn, ctx, this_ptr, args, extras, 0)
406 }
407 }
408 }
409 fn _call_with_extra_args<const MOVE_PTR: bool, const N: usize, const E: usize>(
412 &self,
413 caller_fn: &str,
414 ctx: &NativeCallContext,
415 mut this_ptr: Option<&mut Dynamic>,
416 args: [Dynamic; N],
417 extras: [Dynamic; E],
418 move_this_ptr_to_args: usize,
419 ) -> RhaiResult {
420 match self.typ {
421 #[cfg(not(feature = "no_function"))]
422 FnPtrType::Script(ref fn_def) => {
423 let arity = fn_def.params.len();
424
425 if arity == N + self.curry().len() {
426 return self.call_raw(ctx, this_ptr, args);
427 }
428 if MOVE_PTR && this_ptr.is_some() {
429 if arity == N + 1 + self.curry().len() {
430 let mut args2 = FnArgsVec::with_capacity(args.len() + 1);
431 if move_this_ptr_to_args == 0 {
432 args2.push(this_ptr.as_mut().unwrap().clone());
433 args2.extend(args);
434 } else {
435 args2.extend(args);
436 args2.insert(move_this_ptr_to_args, this_ptr.as_mut().unwrap().clone());
437 }
438 return self.call_raw(ctx, None, args2);
439 }
440 if arity == N + E + 1 + self.curry().len() {
441 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1);
442 if move_this_ptr_to_args == 0 {
443 args2.push(this_ptr.as_mut().unwrap().clone());
444 args2.extend(args);
445 } else {
446 args2.extend(args);
447 args2.insert(move_this_ptr_to_args, this_ptr.as_mut().unwrap().clone());
448 }
449 args2.extend(extras);
450 return self.call_raw(ctx, None, args2);
451 }
452 }
453 if arity == N + E + self.curry().len() {
454 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len());
455 args2.extend(args);
456 args2.extend(extras);
457 return self.call_raw(ctx, this_ptr, args2);
458 }
459 }
460 _ => (),
461 }
462
463 self.call_raw(ctx, this_ptr.as_deref_mut(), args.clone())
464 .or_else(|err| match *err {
465 ERR::ErrorFunctionNotFound(sig, ..)
466 if MOVE_PTR && this_ptr.is_some() && sig.starts_with(self.fn_name()) =>
467 {
468 let mut args2 = FnArgsVec::with_capacity(args.len() + 1);
469 if move_this_ptr_to_args == 0 {
470 args2.push(this_ptr.as_mut().unwrap().clone());
471 args2.extend(args.clone());
472 } else {
473 args2.extend(args.clone());
474 args2.insert(move_this_ptr_to_args, this_ptr.as_mut().unwrap().clone());
475 }
476 self.call_raw(ctx, None, args2)
477 }
478 _ => Err(err),
479 })
480 .or_else(|err| match *err {
481 ERR::ErrorFunctionNotFound(sig, ..) if sig.starts_with(self.fn_name()) => {
482 if MOVE_PTR {
483 if let Some(ref mut this_ptr) = this_ptr {
484 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1);
485 if move_this_ptr_to_args == 0 {
486 args2.push(this_ptr.clone());
487 args2.extend(args);
488 args2.extend(extras);
489 } else {
490 args2.extend(args);
491 args2.extend(extras);
492 args2.insert(move_this_ptr_to_args, this_ptr.clone());
493 }
494 return self.call_raw(ctx, None, args2);
495 }
496 }
497
498 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len());
499 args2.extend(args);
500 args2.extend(extras);
501
502 self.call_raw(ctx, this_ptr, args2)
503 }
504 _ => Err(err),
505 })
506 .map_err(|err| {
507 Box::new(ERR::ErrorInFunctionCall(
508 caller_fn.to_string(),
509 ctx.call_source().unwrap_or("").to_string(),
510 err,
511 Position::NONE,
512 ))
513 })
514 }
515}
516
517impl TryFrom<ImmutableString> for FnPtr {
518 type Error = RhaiError;
519
520 #[inline(always)]
521 fn try_from(value: ImmutableString) -> RhaiResultOf<Self> {
522 if is_valid_function_name(&value) {
523 Ok(Self {
524 name: value,
525 curry: ThinVec::new(),
526 #[cfg(not(feature = "no_function"))]
527 env: None,
528 typ: FnPtrType::Normal,
529 })
530 } else if is_reserved_keyword_or_symbol(&value).0
531 || Token::lookup_symbol_from_syntax(&value).is_some()
532 {
533 Err(ERR::ErrorParsing(PERR::Reserved(value.to_string()), Position::NONE).into())
534 } else {
535 Err(ERR::ErrorFunctionNotFound(value.to_string(), Position::NONE).into())
536 }
537 }
538}
539
540#[cfg(not(feature = "no_function"))]
541impl<T: Into<Shared<crate::ast::ScriptFuncDef>>> From<T> for FnPtr {
542 #[inline(always)]
543 fn from(value: T) -> Self {
544 let fn_def = value.into();
545
546 Self {
547 name: fn_def.name.clone(),
548 curry: ThinVec::new(),
549 #[cfg(not(feature = "no_function"))]
550 env: None,
551 typ: FnPtrType::Script(fn_def),
552 }
553 }
554}
555
556impl Index<usize> for FnPtr {
557 type Output = Dynamic;
558
559 #[inline(always)]
560 fn index(&self, index: usize) -> &Self::Output {
561 self.curry.index(index)
562 }
563}
564
565impl IndexMut<usize> for FnPtr {
566 #[inline(always)]
567 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
568 self.curry.index_mut(index)
569 }
570}
571
572impl Extend<Dynamic> for FnPtr {
573 #[inline(always)]
574 fn extend<T: IntoIterator<Item = Dynamic>>(&mut self, iter: T) {
575 self.curry.extend(iter);
576 }
577}