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