1use crate::{
4 atom::PredefinedAtom,
5 class::{Class, JsClass},
6 function::ffi::RustFunc,
7 qjs, Ctx, Error, FromJs, IntoJs, Object, Result, Value,
8};
9
10mod args;
11mod ffi;
12mod into_func;
13mod params;
14mod types;
15
16pub use args::{Args, IntoArg, IntoArgs};
17pub use ffi::RustFunction;
18pub use params::{FromParam, FromParams, ParamRequirement, Params, ParamsAccessor};
19#[cfg(feature = "futures")]
20pub use types::Async;
21pub use types::{Exhaustive, Flat, Func, FuncArg, MutFn, Null, OnceFn, Opt, Rest, This};
22
23pub trait IntoJsFunc<'js, P> {
25 fn param_requirements() -> ParamRequirement;
28
29 fn call<'a>(&self, params: Params<'a, 'js>) -> Result<Value<'js>>;
31}
32
33pub trait StaticJsFunction {
36 fn call<'a, 'js>(params: Params<'a, 'js>) -> Result<Value<'js>>;
37}
38
39#[derive(Clone, Debug, PartialEq, Eq, Hash)]
41#[repr(transparent)]
42pub struct Function<'js>(pub(crate) Object<'js>);
43
44impl<'js> Function<'js> {
45 pub fn new<P, F>(ctx: Ctx<'js>, f: F) -> Result<Self>
47 where
48 F: IntoJsFunc<'js, P> + 'js,
49 {
50 let func = Box::new(move |params: Params<'_, 'js>| {
51 params.check_params(F::param_requirements())?;
52 f.call(params)
53 }) as Box<dyn RustFunc<'js> + 'js>;
54
55 let cls = Class::instance(ctx, RustFunction(func))?;
56 debug_assert!(cls.is_function());
57 Function(cls.into_inner()).with_length(F::param_requirements().min())
58 }
59
60 pub fn call<A, R>(&self, args: A) -> Result<R>
62 where
63 A: IntoArgs<'js>,
64 R: FromJs<'js>,
65 {
66 let ctx = self.ctx();
67 let num = args.num_args();
68 let mut accum_args = Args::new(ctx.clone(), num);
69 args.into_args(&mut accum_args)?;
70 self.call_arg(accum_args)
71 }
72
73 pub fn call_arg<R>(&self, args: Args<'js>) -> Result<R>
75 where
76 R: FromJs<'js>,
77 {
78 args.apply(self)
79 }
80
81 pub fn defer<A>(&self, args: A) -> Result<()>
86 where
87 A: IntoArgs<'js>,
88 {
89 let ctx = self.ctx();
90 let num = args.num_args();
91 let mut accum_args = Args::new(ctx.clone(), num);
92 args.into_args(&mut accum_args)?;
93 self.defer_arg(accum_args)?;
94 Ok(())
95 }
96
97 pub fn defer_arg(&self, args: Args<'js>) -> Result<()> {
99 args.defer(self.clone())
100 }
101
102 pub fn set_name<S: AsRef<str>>(&self, name: S) -> Result<()> {
104 let name = name.as_ref().into_js(self.ctx())?;
105 unsafe {
106 let res = qjs::JS_DefinePropertyValue(
107 self.0.ctx.as_ptr(),
108 self.0.as_js_value(),
109 PredefinedAtom::Name as qjs::JSAtom,
110 name.into_js_value(),
111 (qjs::JS_PROP_CONFIGURABLE | qjs::JS_PROP_THROW) as _,
112 );
113 if res < 0 {
114 return Err(self.0.ctx.raise_exception());
115 }
116 };
117 Ok(())
118 }
119
120 pub fn with_name<S: AsRef<str>>(self, name: S) -> Result<Self> {
122 self.set_name(name)?;
123 Ok(self)
124 }
125
126 pub fn set_length(&self, len: usize) -> Result<()> {
128 let len = len.into_js(self.ctx())?;
129 unsafe {
130 let res = qjs::JS_DefinePropertyValue(
131 self.0.ctx.as_ptr(),
132 self.0.as_js_value(),
133 PredefinedAtom::Length as qjs::JSAtom,
134 len.into_js_value(),
135 (qjs::JS_PROP_CONFIGURABLE | qjs::JS_PROP_THROW) as _,
136 );
137 if res < 0 {
138 return Err(self.0.ctx.raise_exception());
139 }
140 };
141 Ok(())
142 }
143
144 pub fn with_length(self, len: usize) -> Result<Self> {
146 self.set_length(len)?;
147 Ok(self)
148 }
149
150 pub fn prototype(ctx: Ctx<'js>) -> Object<'js> {
153 let res = unsafe {
154 let v = qjs::JS_GetFunctionProto(ctx.as_ptr());
155 Value::from_js_value(ctx, v)
156 };
157 res.into_object()
159 .expect("`Function.prototype` wasn't an object")
160 }
161
162 pub fn is_constructor(&self) -> bool {
164 ((unsafe { qjs::JS_IsConstructor(self.ctx().as_ptr(), self.0.as_js_value()) }) as i32) != 0
165 }
166
167 pub fn set_constructor(&self, is_constructor: bool) {
169 unsafe {
170 qjs::JS_SetConstructorBit(
171 self.ctx().as_ptr(),
172 self.0.as_js_value(),
173 is_constructor.into(),
174 )
175 };
176 }
177
178 pub fn with_constructor(self, is_constructor: bool) -> Self {
180 self.set_constructor(is_constructor);
181 self
182 }
183}
184
185#[derive(Debug, Clone)]
189#[repr(transparent)]
190pub struct Constructor<'js>(pub(crate) Function<'js>);
191
192impl<'js> Constructor<'js> {
193 pub fn new_class<C, F, P>(ctx: Ctx<'js>, f: F) -> Result<Self>
198 where
199 F: IntoJsFunc<'js, P> + 'js,
200 C: JsClass<'js>,
201 {
202 let func = Box::new(move |params: Params<'_, 'js>| -> Result<Value<'js>> {
203 params.check_params(F::param_requirements())?;
204 let this = params.this();
205 let ctx = params.ctx().clone();
206
207 let proto = this
209 .into_function()
210 .map(|func| func.get(PredefinedAtom::Prototype))
211 .unwrap_or_else(|| Class::<C>::prototype(&ctx))?;
212
213 let res = f.call(params)?;
214 res.as_object()
215 .ok_or_else(|| Error::IntoJs {
216 from: res.type_of().as_str(),
217 to: "object",
218 message: Some("rust constructor function did not return a object".to_owned()),
219 })?
220 .set_prototype(proto.as_ref())?;
221 Ok(res)
222 });
223 let func = Function(Class::instance(ctx.clone(), RustFunction(func))?.into_inner())
224 .with_name(C::NAME)?
225 .with_constructor(true);
226 unsafe {
227 qjs::JS_SetConstructor(
228 ctx.as_ptr(),
229 func.as_js_value(),
230 Class::<C>::prototype(&ctx)?
231 .as_ref()
232 .map(|x| x.as_js_value())
233 .unwrap_or(qjs::JS_NULL),
234 )
235 };
236 Ok(Constructor(func))
237 }
238
239 pub fn new_prototype<F, P>(ctx: &Ctx<'js>, prototype: Object<'js>, f: F) -> Result<Self>
243 where
244 F: IntoJsFunc<'js, P> + 'js,
245 {
246 let proto_clone = prototype.clone();
247 let func = Box::new(move |params: Params<'_, 'js>| -> Result<Value<'js>> {
248 params.check_params(F::param_requirements())?;
249 let this = params.this();
250 let proto = this
251 .as_function()
252 .map(|func| func.get(PredefinedAtom::Prototype))
253 .unwrap_or_else(|| Ok(Some(proto_clone.clone())))?;
254
255 let res = f.call(params)?;
256 res.as_object()
257 .ok_or_else(|| Error::IntoJs {
258 from: res.type_of().as_str(),
259 to: "object",
260 message: Some("rust constructor function did not return a object".to_owned()),
261 })?
262 .set_prototype(proto.as_ref())?;
263 Ok(res)
264 });
265 let func = Function(Class::instance(ctx.clone(), RustFunction(func))?.into_inner())
266 .with_constructor(true);
267 unsafe {
268 qjs::JS_SetConstructor(ctx.as_ptr(), func.as_js_value(), prototype.as_js_value())
269 };
270 Ok(Constructor(func))
271 }
272
273 pub fn construct<A, R>(&self, args: A) -> Result<R>
277 where
278 A: IntoArgs<'js>,
279 R: FromJs<'js>,
280 {
281 let ctx = self.ctx();
282 let num = args.num_args();
283 let mut accum_args = Args::new(ctx.clone(), num);
284 args.into_args(&mut accum_args)?;
285 self.construct_args(accum_args)
286 }
287
288 pub fn construct_args<R>(&self, args: Args<'js>) -> Result<R>
292 where
293 R: FromJs<'js>,
294 {
295 args.construct(self)
296 }
297}
298
299#[cfg(test)]
300mod test {
301 use crate::{prelude::*, *};
302 use approx::assert_abs_diff_eq as assert_approx_eq;
303
304 #[test]
305 fn call_js_fn_with_no_args_and_no_return() {
306 test_with(|ctx| {
307 let f: Function = ctx.eval("() => {}").unwrap();
308
309 let _: () = ().apply(&f).unwrap();
310 let _: () = f.call(()).unwrap();
311 })
312 }
313
314 #[test]
315 fn call_js_fn_with_no_args_and_return() {
316 test_with(|ctx| {
317 let f: Function = ctx.eval("() => 42").unwrap();
318
319 let res: i32 = ().apply(&f).unwrap();
320 assert_eq!(res, 42);
321
322 let res: i32 = f.call(()).unwrap();
323 assert_eq!(res, 42);
324 })
325 }
326
327 #[test]
328 fn call_js_fn_with_1_arg_and_return() {
329 test_with(|ctx| {
330 let f: Function = ctx.eval("a => a + 4").unwrap();
331
332 let res: i32 = (3,).apply(&f).unwrap();
333 assert_eq!(res, 7);
334
335 let res: i32 = f.call((1,)).unwrap();
336 assert_eq!(res, 5);
337 })
338 }
339
340 #[test]
341 fn call_js_fn_with_2_args_and_return() {
342 test_with(|ctx| {
343 let f: Function = ctx.eval("(a, b) => a * b + 4").unwrap();
344
345 let res: i32 = (3, 4).apply(&f).unwrap();
346 assert_eq!(res, 16);
347
348 let res: i32 = f.call((5, 1)).unwrap();
349 assert_eq!(res, 9);
350 })
351 }
352
353 #[test]
354 fn call_js_fn_with_var_args_and_return() {
355 let res: Vec<i8> = test_with(|ctx| {
356 let func: Function = ctx
357 .eval(
358 r#"
359 (...x) => [x.length, ...x]
360 "#,
361 )
362 .unwrap();
363 func.call((Rest(vec![1, 2, 3]),)).unwrap()
364 });
365 assert_eq!(res.len(), 4);
366 assert_eq!(res[0], 3);
367 assert_eq!(res[1], 1);
368 assert_eq!(res[2], 2);
369 assert_eq!(res[3], 3);
370 }
371
372 #[test]
373 fn call_js_fn_with_rest_args_and_return() {
374 let res: Vec<i8> = test_with(|ctx| {
375 let func: Function = ctx
376 .eval(
377 r#"
378 (a, b, ...x) => [a, b, x.length, ...x]
379 "#,
380 )
381 .unwrap();
382 func.call((-2, -1, Rest(vec![1, 2]))).unwrap()
383 });
384 assert_eq!(res.len(), 5);
385 assert_eq!(res[0], -2);
386 assert_eq!(res[1], -1);
387 assert_eq!(res[2], 2);
388 assert_eq!(res[3], 1);
389 assert_eq!(res[4], 2);
390 }
391
392 #[test]
393 fn call_js_fn_with_no_args_and_throw() {
394 test_with(|ctx| {
395 let f: Function = ctx
396 .eval("() => { throw new Error('unimplemented'); }")
397 .unwrap();
398
399 if let Err(Error::Exception) = f.call::<_, ()>(()) {
400 let exception = Exception::from_js(&ctx, ctx.catch()).unwrap();
401 assert_eq!(exception.message().as_deref(), Some("unimplemented"));
402 } else {
403 panic!("Should throws");
404 }
405 })
406 }
407
408 #[test]
409 fn call_js_fn_with_this_and_no_args_and_return() {
410 test_with(|ctx| {
411 let f: Function = ctx.eval("function f() { return this.val; } f").unwrap();
412 let obj = Object::new(ctx).unwrap();
413 obj.set("val", 42).unwrap();
414
415 let res: i32 = (This(obj.clone()),).apply(&f).unwrap();
416 assert_eq!(res, 42);
417 let res: i32 = f.call((This(obj),)).unwrap();
418 assert_eq!(res, 42);
419 })
420 }
421
422 #[test]
423 fn call_js_fn_with_this_and_1_arg_and_return() {
424 test_with(|ctx| {
425 let f: Function = ctx
426 .eval("function f(a) { return this.val * a; } f")
427 .unwrap();
428 let obj = Object::new(ctx).unwrap();
429 obj.set("val", 3).unwrap();
430
431 let res: i32 = (This(obj.clone()), 2).apply(&f).unwrap();
432 assert_eq!(res, 6);
433 let res: i32 = f.call((This(obj), 3)).unwrap();
434 assert_eq!(res, 9);
435 })
436 }
437
438 #[test]
439 fn call_js_fn_with_1_arg_deferred() {
440 let rt = Runtime::new().unwrap();
441 let ctx = Context::full(&rt).unwrap();
442 assert!(!rt.is_job_pending());
443 ctx.with(|ctx| {
444 let g = ctx.globals();
445 let f: Function = ctx.eval("(obj) => { obj.called = true; }").unwrap();
446 f.defer((g.clone(),)).unwrap();
447 let c: Value = g.get("called").unwrap();
448 assert_eq!(c.type_of(), Type::Undefined);
449 });
450 assert!(rt.is_job_pending());
451 rt.execute_pending_job().unwrap();
452 ctx.with(|ctx| {
453 let g = ctx.globals();
454 let c: Value = g.get("called").unwrap();
455 assert_eq!(c.type_of(), Type::Bool);
456 });
457 }
458
459 fn test() {
460 println!("test");
461 }
462
463 #[test]
464 fn static_callback() {
465 test_with(|ctx| {
466 let f = Function::new(ctx.clone(), test).unwrap();
467 f.set_name("test").unwrap();
468 let eval: Function = ctx.eval("a => { a() }").unwrap();
469 (f.clone(),).apply::<()>(&eval).unwrap();
470 f.call::<_, ()>(()).unwrap();
471
472 let name: StdString = f.clone().into_inner().get("name").unwrap();
473 assert_eq!(name, "test");
474
475 let get_name: Function = ctx.eval("a => a.name").unwrap();
476 let name: StdString = get_name.call((f.clone(),)).unwrap();
477 assert_eq!(name, "test");
478 })
479 }
480
481 #[test]
482 fn const_callback() {
483 use std::sync::{Arc, Mutex};
484 test_with(|ctx| {
485 #[allow(clippy::mutex_atomic)]
486 let called = Arc::new(Mutex::new(false));
487 let called_clone = called.clone();
488 let f = Function::new(ctx.clone(), move || {
489 (*called_clone.lock().unwrap()) = true;
490 })
491 .unwrap();
492 f.set_name("test").unwrap();
493
494 let eval: Function = ctx.eval("a => { a() }").unwrap();
495 eval.call::<_, ()>((f.clone(),)).unwrap();
496 f.call::<_, ()>(()).unwrap();
497 assert!(*called.lock().unwrap());
498
499 let name: StdString = f.clone().into_inner().get("name").unwrap();
500 assert_eq!(name, "test");
501
502 let get_name: Function = ctx.eval("a => a.name").unwrap();
503 let name: StdString = get_name.call((f.clone(),)).unwrap();
504 assert_eq!(name, "test");
505 })
506 }
507
508 #[test]
509 fn mutable_callback() {
510 test_with(|ctx| {
511 let mut v = 0;
512 let f = Function::new(
513 ctx.clone(),
514 MutFn::new(move || {
515 v += 1;
516 v
517 }),
518 )
519 .unwrap();
520 f.set_name("test").unwrap();
521
522 let eval: Function = ctx.eval("a => a()").unwrap();
523 assert_eq!(eval.call::<_, i32>((f.clone(),)).unwrap(), 1);
524 assert_eq!(eval.call::<_, i32>((f.clone(),)).unwrap(), 2);
525 assert_eq!(eval.call::<_, i32>((f.clone(),)).unwrap(), 3);
526
527 let name: StdString = f.clone().into_inner().get("name").unwrap();
528 assert_eq!(name, "test");
529
530 let get_name: Function = ctx.eval("a => a.name").unwrap();
531 let name: StdString = get_name.call((f.clone(),)).unwrap();
532 assert_eq!(name, "test");
533 })
534 }
535
536 #[test]
537 #[should_panic(
538 expected = "Error borrowing function: can't borrow a value as it is already borrowed"
539 )]
540 fn recursively_called_mutable_callback() {
541 test_with(|ctx| {
542 let mut v = 0;
543 let f = Function::new(
544 ctx.clone(),
545 MutFn::new(move |ctx: Ctx| {
546 v += 1;
547 ctx.globals()
548 .get::<_, Function>("foo")
549 .unwrap()
550 .call::<_, ()>(())
551 .catch(&ctx)
552 .unwrap();
553 v
554 }),
555 )
556 .unwrap();
557 ctx.globals().set("foo", f.clone()).unwrap();
558 f.call::<_, ()>(()).unwrap();
559 })
560 }
561
562 #[test]
563 #[should_panic(
564 expected = "Error borrowing function: tried to use a value, which can only be used once, again."
565 )]
566 fn repeatedly_called_once_callback() {
567 test_with(|ctx| {
568 let mut v = 0;
569 let f = Function::new(
570 ctx.clone(),
571 OnceFn::from(move || {
572 v += 1;
573 v
574 }),
575 )
576 .unwrap();
577 ctx.globals().set("foo", f.clone()).unwrap();
578 f.call::<_, ()>(()).catch(&ctx).unwrap();
579 f.call::<_, ()>(()).catch(&ctx).unwrap();
580 })
581 }
582
583 #[test]
584 fn multiple_const_callbacks() {
585 test_with(|ctx| {
586 let globals = ctx.globals();
587 globals.set("one", Func::new(|| 1f64)).unwrap();
588 globals.set("neg", Func::new(|a: f64| -a)).unwrap();
589 globals
590 .set("add", Func::new(|a: f64, b: f64| a + b))
591 .unwrap();
592
593 let r: f64 = ctx.eval("neg(add(one(), 2))").unwrap();
594 assert_approx_eq!(r, -3.0);
595 })
596 }
597
598 #[test]
599 fn mutable_callback_which_can_fail() {
600 test_with(|ctx| {
601 let globals = ctx.globals();
602 let mut id_alloc = 0;
603 globals
604 .set(
605 "new_id",
606 Func::from(MutFn::from(move || {
607 id_alloc += 1;
608 if id_alloc < 4 {
609 Ok(id_alloc)
610 } else {
611 Err(Error::Unknown)
612 }
613 })),
614 )
615 .unwrap();
616
617 let id: u32 = ctx.eval("new_id()").unwrap();
618 assert_eq!(id, 1);
619 let id: u32 = ctx.eval("new_id()").unwrap();
620 assert_eq!(id, 2);
621 let id: u32 = ctx.eval("new_id()").unwrap();
622 assert_eq!(id, 3);
623 let _err = ctx.eval::<u32, _>("new_id()").unwrap_err();
624 })
625 }
626
627 #[test]
628 fn mutable_callback_with_ctx_which_reads_globals() {
629 test_with(|ctx| {
630 let globals = ctx.globals();
631 let mut id_alloc = 0;
632 globals
633 .set(
634 "new_id",
635 Func::from(MutFn::from(move |ctx: Ctx| {
636 let initial: Option<u32> = ctx.globals().get("initial_id")?;
637 if let Some(initial) = initial {
638 id_alloc += 1;
639 Ok(id_alloc + initial)
640 } else {
641 Err(Error::Unknown)
642 }
643 })),
644 )
645 .unwrap();
646
647 let _err = ctx.eval::<u32, _>("new_id()").unwrap_err();
648 globals.set("initial_id", 10).unwrap();
649
650 let id: u32 = ctx.eval("new_id()").unwrap();
651 assert_eq!(id, 11);
652 let id: u32 = ctx.eval("new_id()").unwrap();
653 assert_eq!(id, 12);
654 let id: u32 = ctx.eval("new_id()").unwrap();
655 assert_eq!(id, 13);
656 })
657 }
658
659 #[test]
660 fn call_rust_fn_with_ctx_and_value() {
661 test_with(|ctx| {
662 let func = Func::from(|ctx, val| {
663 struct Args<'js>(Ctx<'js>, Value<'js>);
664 let Args(ctx, val) = Args(ctx, val);
665 ctx.globals().set("test_str", val).unwrap();
666 });
667 ctx.globals().set("test_fn", func).unwrap();
668 ctx.eval::<(), _>(
669 r#"
670 test_fn("test_str")
671 "#,
672 )
673 .unwrap();
674 let val: StdString = ctx.globals().get("test_str").unwrap();
675 assert_eq!(val, "test_str");
676 });
677 }
678
679 #[test]
680 fn call_rust_fn_with_this_and_args() {
681 let res: f64 = test_with(|ctx| {
682 let func = Function::new(ctx.clone(), |this: This<Object>, a: f64, b: f64| {
683 let x: f64 = this.get("x").unwrap();
684 let y: f64 = this.get("y").unwrap();
685 this.set("r", a * x + b * y).unwrap();
686 })
687 .unwrap();
688 ctx.globals().set("test_fn", func).unwrap();
689 ctx.eval(
690 r#"
691 let test_obj = { x: 1, y: 2 };
692 test_fn.call(test_obj, 3, 4);
693 test_obj.r
694 "#,
695 )
696 .unwrap()
697 });
698 assert_eq!(res, 11.0);
699 }
700
701 #[test]
702 fn apply_rust_fn_with_this_and_args() {
703 let res: f32 = test_with(|ctx| {
704 let func = Function::new(ctx.clone(), |this: This<Object>, x: f32, y: f32| {
705 let a: f32 = this.get("a").unwrap();
706 let b: f32 = this.get("b").unwrap();
707 a * x + b * y
708 })
709 .unwrap();
710 ctx.globals().set("test_fn", func).unwrap();
711 ctx.eval(
712 r#"
713 let test_obj = { a: 1, b: 2 };
714 test_fn.apply(test_obj, [3, 4])
715 "#,
716 )
717 .unwrap()
718 });
719 assert_eq!(res, 11.0);
720 }
721
722 #[test]
723 fn bind_rust_fn_with_this_and_call_with_args() {
724 let res: f32 = test_with(|ctx| {
725 let func = Function::new(ctx.clone(), |this: This<Object>, x: f32, y: f32| {
726 let a: f32 = this.get("a").unwrap();
727 let b: f32 = this.get("b").unwrap();
728 a * x + b * y
729 })
730 .unwrap();
731 ctx.globals().set("test_fn", func).unwrap();
732 ctx.eval(
733 r#"
734 let test_obj = { a: 1, b: 2 };
735 test_fn.bind(test_obj)(3, 4)
736 "#,
737 )
738 .unwrap()
739 });
740 assert_eq!(res, 11.0);
741 }
742
743 #[test]
744 fn call_rust_fn_with_var_args() {
745 let res: Vec<i8> = test_with(|ctx| {
746 let func = Function::new(ctx.clone(), |args: Rest<i8>| {
747 use std::iter::once;
748 once(args.len() as i8)
749 .chain(args.iter().cloned())
750 .collect::<Vec<_>>()
751 })
752 .unwrap();
753 ctx.globals().set("test_fn", func).unwrap();
754 ctx.eval(
755 r#"
756 test_fn(1, 2, 3)
757 "#,
758 )
759 .unwrap()
760 });
761 assert_eq!(res.len(), 4);
762 assert_eq!(res[0], 3);
763 assert_eq!(res[1], 1);
764 assert_eq!(res[2], 2);
765 assert_eq!(res[3], 3);
766 }
767
768 #[test]
769 fn call_rust_fn_with_rest_args() {
770 let res: Vec<i8> = test_with(|ctx| {
771 let func = Function::new(ctx.clone(), |arg1: i8, arg2: i8, args: Rest<i8>| {
772 use std::iter::once;
773 once(arg1)
774 .chain(once(arg2))
775 .chain(once(args.len() as i8))
776 .chain(args.iter().cloned())
777 .collect::<Vec<_>>()
778 })
779 .unwrap();
780 ctx.globals().set("test_fn", func).unwrap();
781 ctx.eval(
782 r#"
783 test_fn(-2, -1, 1, 2)
784 "#,
785 )
786 .unwrap()
787 });
788 assert_eq!(res.len(), 5);
789 assert_eq!(res[0], -2);
790 assert_eq!(res[1], -1);
791 assert_eq!(res[2], 2);
792 assert_eq!(res[3], 1);
793 assert_eq!(res[4], 2);
794 }
795
796 #[test]
797 fn js_fn_wrappers() {
798 test_with(|ctx| {
799 let global = ctx.globals();
800 global
801 .set(
802 "cat",
803 Func::from(|a: StdString, b: StdString| format!("{a}{b}")),
804 )
805 .unwrap();
806 let res: StdString = ctx.eval("cat(\"foo\", \"bar\")").unwrap();
807 assert_eq!(res, "foobar");
808
809 let mut log = Vec::<StdString>::new();
810 global
811 .set(
812 "log",
813 Func::from(MutFn::from(move |msg: StdString| {
814 log.push(msg);
815 log.len() as u32
816 })),
817 )
818 .unwrap();
819 let n: u32 = ctx.eval("log(\"foo\") + log(\"bar\")").unwrap();
820 assert_eq!(n, 3);
821 });
822 }
823}