1use crate::ast::EncapsulatedEnviron;
4use crate::eval::GlobalRuntimeState;
5use crate::tokenizer::{is_reserved_keyword_or_symbol, is_valid_function_name, Token};
6use crate::types::dynamic::Variant;
7use crate::{
8 Dynamic, Engine, FnArgsVec, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError,
9 RhaiResult, RhaiResultOf, Shared, StaticVec, ThinVec, 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)]
23pub struct FnPtr {
24 pub(crate) name: ImmutableString,
25 pub(crate) curry: ThinVec<Dynamic>,
26 pub(crate) environ: Option<Shared<EncapsulatedEnviron>>,
27 #[cfg(not(feature = "no_function"))]
28 pub(crate) fn_def: Option<Shared<crate::ast::ScriptFuncDef>>,
29}
30
31impl fmt::Display for FnPtr {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 write!(f, "Fn({})", self.fn_name())
34 }
35}
36
37impl fmt::Debug for FnPtr {
38 #[cold]
39 #[inline(never)]
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 let func = "Fn";
42 #[cfg(not(feature = "no_function"))]
43 let func = if self.fn_def.is_some() { "Fn*" } else { func };
44
45 let ff = &mut f.debug_tuple(func);
46 ff.field(&self.name);
47 self.curry.iter().for_each(|curry| {
48 ff.field(curry);
49 });
50 ff.finish()?;
51
52 Ok(())
53 }
54}
55
56impl FnPtr {
57 #[inline(always)]
59 pub fn new(name: impl Into<ImmutableString>) -> RhaiResultOf<Self> {
60 name.into().try_into()
61 }
62 #[inline(always)]
64 #[must_use]
65 pub fn fn_name(&self) -> &str {
66 self.fn_name_raw()
67 }
68 #[inline(always)]
70 #[must_use]
71 pub(crate) const fn fn_name_raw(&self) -> &ImmutableString {
72 &self.name
73 }
74 #[inline(always)]
76 pub fn curry(&self) -> &[Dynamic] {
77 self.curry.as_ref()
78 }
79 #[inline(always)]
81 pub fn iter_curry(&self) -> impl Iterator<Item = &Dynamic> {
82 self.curry.iter()
83 }
84 #[inline(always)]
86 pub fn iter_curry_mut(&mut self) -> impl Iterator<Item = &mut Dynamic> {
87 self.curry.iter_mut()
88 }
89 #[inline(always)]
91 pub fn add_curry(&mut self, value: Dynamic) -> &mut Self {
92 self.curry.push(value);
93 self
94 }
95 #[inline]
97 pub fn set_curry(&mut self, values: impl IntoIterator<Item = Dynamic>) -> &mut Self {
98 self.curry = values.into_iter().collect();
99 self
100 }
101 #[inline(always)]
103 #[must_use]
104 pub fn is_curried(&self) -> bool {
105 !self.curry.is_empty()
106 }
107 #[cfg(not(feature = "no_function"))]
111 #[inline(always)]
112 #[must_use]
113 pub fn is_anonymous(&self) -> bool {
114 crate::func::is_anonymous_fn(&self.name)
115 }
116 #[inline]
148 pub fn call<T: Variant + Clone>(
149 &self,
150 engine: &Engine,
151 ast: &AST,
152 args: impl FuncArgs,
153 ) -> RhaiResultOf<T> {
154 let _ast = ast;
155 let mut arg_values = StaticVec::new_const();
156 args.parse(&mut arg_values);
157
158 let global = &mut GlobalRuntimeState::new(engine);
159
160 #[cfg(not(feature = "no_function"))]
161 global.lib.push(_ast.shared_lib().clone());
162
163 let ctx = (engine, self.fn_name(), None, &*global, Position::NONE).into();
164
165 self.call_raw(&ctx, None, arg_values).and_then(|result| {
166 result.try_cast_raw().map_err(|r| {
167 let result_type = engine.map_type_name(r.type_name());
168 let cast_type = match type_name::<T>() {
169 typ if typ.contains("::") => engine.map_type_name(typ),
170 typ => typ,
171 };
172 ERR::ErrorMismatchOutputType(cast_type.into(), result_type.into(), Position::NONE)
173 .into()
174 })
175 })
176 }
177 #[inline]
184 pub fn call_within_context<T: Variant + Clone>(
185 &self,
186 context: &NativeCallContext,
187 args: impl FuncArgs,
188 ) -> RhaiResultOf<T> {
189 let mut arg_values = StaticVec::new_const();
190 args.parse(&mut arg_values);
191
192 self.call_raw(context, None, arg_values).and_then(|result| {
193 result.try_cast_raw().map_err(|r| {
194 let result_type = context.engine().map_type_name(r.type_name());
195 let cast_type = match type_name::<T>() {
196 typ if typ.contains("::") => context.engine().map_type_name(typ),
197 typ => typ,
198 };
199 ERR::ErrorMismatchOutputType(cast_type.into(), result_type.into(), Position::NONE)
200 .into()
201 })
202 })
203 }
204 #[inline]
223 pub fn call_raw(
224 &self,
225 context: &NativeCallContext,
226 this_ptr: Option<&mut Dynamic>,
227 arg_values: impl AsMut<[Dynamic]>,
228 ) -> RhaiResult {
229 let mut arg_values = arg_values;
230 let mut arg_values = arg_values.as_mut();
231 let mut args_data;
232
233 if self.is_curried() {
234 args_data = FnArgsVec::with_capacity(self.curry().len() + arg_values.len());
235 args_data.extend(self.curry().iter().cloned());
236 args_data.extend(arg_values.iter_mut().map(mem::take));
237 arg_values = &mut *args_data;
238 };
239
240 let args = &mut StaticVec::with_capacity(arg_values.len() + 1);
241 args.extend(arg_values.iter_mut());
242
243 #[cfg(not(feature = "no_function"))]
245 match self.fn_def {
246 Some(ref fn_def) if fn_def.params.len() == args.len() => {
247 let global = &mut context.global_runtime_state().clone();
248 global.level += 1;
249
250 let caches = &mut crate::eval::Caches::new();
251
252 return context.engine().call_script_fn(
253 global,
254 caches,
255 &mut crate::Scope::new(),
256 this_ptr,
257 self.environ.as_deref(),
258 fn_def,
259 args,
260 true,
261 context.position(),
262 );
263 }
264 _ => (),
265 }
266
267 let is_method = this_ptr.is_some();
268
269 if let Some(obj) = this_ptr {
270 args.insert(0, obj);
271 }
272
273 context.call_fn_raw(self.fn_name(), is_method, is_method, args)
274 }
275
276 #[cfg(not(feature = "internals"))]
290 #[inline(always)]
291 #[allow(dead_code)]
292 pub(crate) fn call_raw_with_extra_args<const N: usize, const E: usize>(
293 &self,
294 fn_name: &str,
295 ctx: &NativeCallContext,
296 this_ptr: Option<&mut Dynamic>,
297 args: [Dynamic; N],
298 extras: [Dynamic; E],
299 move_this_ptr_to_args: Option<usize>,
300 ) -> RhaiResult {
301 match move_this_ptr_to_args {
302 Some(m) => {
303 self._call_with_extra_args::<true, N, E>(fn_name, ctx, this_ptr, args, extras, m)
304 }
305 None => {
306 self._call_with_extra_args::<false, N, E>(fn_name, ctx, this_ptr, args, extras, 0)
307 }
308 }
309 }
310 #[cfg(feature = "internals")]
325 #[inline(always)]
326 pub fn call_raw_with_extra_args<const N: usize, const E: usize>(
327 &self,
328 fn_name: &str,
329 ctx: &NativeCallContext,
330 this_ptr: Option<&mut Dynamic>,
331 args: [Dynamic; N],
332 extras: [Dynamic; E],
333 move_this_ptr_to_args: Option<usize>,
334 ) -> RhaiResult {
335 match move_this_ptr_to_args {
336 Some(m) => {
337 self._call_with_extra_args::<true, N, E>(fn_name, ctx, this_ptr, args, extras, m)
338 }
339 None => {
340 self._call_with_extra_args::<false, N, E>(fn_name, ctx, this_ptr, args, extras, 0)
341 }
342 }
343 }
344 fn _call_with_extra_args<const MOVE_PTR: bool, const N: usize, const E: usize>(
347 &self,
348 fn_name: &str,
349 ctx: &NativeCallContext,
350 mut this_ptr: Option<&mut Dynamic>,
351 args: [Dynamic; N],
352 extras: [Dynamic; E],
353 move_this_ptr_to_args: usize,
354 ) -> RhaiResult {
355 #[cfg(not(feature = "no_function"))]
356 if let Some(arity) = self.fn_def.as_deref().map(|f| f.params.len()) {
357 if arity == N + self.curry().len() {
358 return self.call_raw(ctx, this_ptr, args);
359 }
360 if MOVE_PTR && this_ptr.is_some() {
361 if arity == N + 1 + self.curry().len() {
362 let mut args2 = FnArgsVec::with_capacity(args.len() + 1);
363 if move_this_ptr_to_args == 0 {
364 args2.push(this_ptr.as_mut().unwrap().clone());
365 args2.extend(args);
366 } else {
367 args2.extend(args);
368 args2.insert(move_this_ptr_to_args, this_ptr.as_mut().unwrap().clone());
369 }
370 return self.call_raw(ctx, None, args2);
371 }
372 if arity == N + E + 1 + self.curry().len() {
373 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1);
374 if move_this_ptr_to_args == 0 {
375 args2.push(this_ptr.as_mut().unwrap().clone());
376 args2.extend(args);
377 } else {
378 args2.extend(args);
379 args2.insert(move_this_ptr_to_args, this_ptr.as_mut().unwrap().clone());
380 }
381 args2.extend(extras);
382 return self.call_raw(ctx, None, args2);
383 }
384 }
385 if arity == N + E + self.curry().len() {
386 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len());
387 args2.extend(args);
388 args2.extend(extras);
389 return self.call_raw(ctx, this_ptr, args2);
390 }
391 }
392
393 self.call_raw(ctx, this_ptr.as_deref_mut(), args.clone())
394 .or_else(|err| match *err {
395 ERR::ErrorFunctionNotFound(sig, ..)
396 if MOVE_PTR && this_ptr.is_some() && sig.starts_with(self.fn_name()) =>
397 {
398 let mut args2 = FnArgsVec::with_capacity(args.len() + 1);
399 if move_this_ptr_to_args == 0 {
400 args2.push(this_ptr.as_mut().unwrap().clone());
401 args2.extend(args.clone());
402 } else {
403 args2.extend(args.clone());
404 args2.insert(move_this_ptr_to_args, this_ptr.as_mut().unwrap().clone());
405 }
406 self.call_raw(ctx, None, args2)
407 }
408 _ => Err(err),
409 })
410 .or_else(|err| match *err {
411 ERR::ErrorFunctionNotFound(sig, ..) if sig.starts_with(self.fn_name()) => {
412 if MOVE_PTR {
413 if let Some(ref mut this_ptr) = this_ptr {
414 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1);
415 if move_this_ptr_to_args == 0 {
416 args2.push(this_ptr.clone());
417 args2.extend(args);
418 args2.extend(extras);
419 } else {
420 args2.extend(args);
421 args2.extend(extras);
422 args2.insert(move_this_ptr_to_args, this_ptr.clone());
423 }
424 return self.call_raw(ctx, None, args2);
425 }
426 }
427
428 let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len());
429 args2.extend(args);
430 args2.extend(extras);
431
432 self.call_raw(ctx, this_ptr, args2)
433 }
434 _ => Err(err),
435 })
436 .map_err(|err| {
437 Box::new(ERR::ErrorInFunctionCall(
438 fn_name.to_string(),
439 ctx.source().unwrap_or("").to_string(),
440 err,
441 Position::NONE,
442 ))
443 })
444 }
445}
446
447impl TryFrom<ImmutableString> for FnPtr {
448 type Error = RhaiError;
449
450 #[inline(always)]
451 fn try_from(value: ImmutableString) -> RhaiResultOf<Self> {
452 if is_valid_function_name(&value) {
453 Ok(Self {
454 name: value,
455 curry: ThinVec::new(),
456 environ: None,
457 #[cfg(not(feature = "no_function"))]
458 fn_def: None,
459 })
460 } else if is_reserved_keyword_or_symbol(&value).0
461 || Token::lookup_symbol_from_syntax(&value).is_some()
462 {
463 Err(ERR::ErrorParsing(PERR::Reserved(value.to_string()), Position::NONE).into())
464 } else {
465 Err(ERR::ErrorFunctionNotFound(value.to_string(), Position::NONE).into())
466 }
467 }
468}
469
470#[cfg(not(feature = "no_function"))]
471impl<T: Into<Shared<crate::ast::ScriptFuncDef>>> From<T> for FnPtr {
472 #[inline(always)]
473 fn from(value: T) -> Self {
474 let fn_def = value.into();
475
476 Self {
477 name: fn_def.name.clone(),
478 curry: ThinVec::new(),
479 environ: None,
480 fn_def: Some(fn_def),
481 }
482 }
483}
484
485impl Index<usize> for FnPtr {
486 type Output = Dynamic;
487
488 #[inline(always)]
489 fn index(&self, index: usize) -> &Self::Output {
490 self.curry.index(index)
491 }
492}
493
494impl IndexMut<usize> for FnPtr {
495 #[inline(always)]
496 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
497 self.curry.index_mut(index)
498 }
499}
500
501impl Extend<Dynamic> for FnPtr {
502 #[inline(always)]
503 fn extend<T: IntoIterator<Item = Dynamic>>(&mut self, iter: T) {
504 self.curry.extend(iter);
505 }
506}