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