summaryrefslogtreecommitdiffstats
path: root/vendor/wasm-bindgen/tests/wasm/closures.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/wasm-bindgen/tests/wasm/closures.rs')
-rw-r--r--vendor/wasm-bindgen/tests/wasm/closures.rs645
1 files changed, 645 insertions, 0 deletions
diff --git a/vendor/wasm-bindgen/tests/wasm/closures.rs b/vendor/wasm-bindgen/tests/wasm/closures.rs
new file mode 100644
index 000000000..dc88b43ad
--- /dev/null
+++ b/vendor/wasm-bindgen/tests/wasm/closures.rs
@@ -0,0 +1,645 @@
+use js_sys::Number;
+use std::cell::{Cell, RefCell};
+use std::rc::Rc;
+use wasm_bindgen::prelude::*;
+use wasm_bindgen_test::*;
+
+#[wasm_bindgen(module = "tests/wasm/closures.js")]
+extern "C" {
+ fn works_call(a: &dyn Fn());
+ fn works_thread(a: &dyn Fn(u32) -> u32) -> u32;
+
+ fn cannot_reuse_call(a: &dyn Fn());
+ #[wasm_bindgen(catch)]
+ fn cannot_reuse_call_again() -> Result<(), JsValue>;
+
+ fn long_lived_call1(a: &Closure<dyn Fn()>);
+ fn long_lived_call2(a: &Closure<dyn FnMut(u32) -> u32>) -> u32;
+
+ fn many_arity_call1(a: &Closure<dyn Fn()>);
+ fn many_arity_call2(a: &Closure<dyn Fn(u32)>);
+ fn many_arity_call3(a: &Closure<dyn Fn(u32, u32)>);
+ fn many_arity_call4(a: &Closure<dyn Fn(u32, u32, u32)>);
+ fn many_arity_call5(a: &Closure<dyn Fn(u32, u32, u32, u32)>);
+ fn many_arity_call6(a: &Closure<dyn Fn(u32, u32, u32, u32, u32)>);
+ fn many_arity_call7(a: &Closure<dyn Fn(u32, u32, u32, u32, u32, u32)>);
+ fn many_arity_call8(a: &Closure<dyn Fn(u32, u32, u32, u32, u32, u32, u32)>);
+ fn many_arity_call9(a: &Closure<dyn Fn(u32, u32, u32, u32, u32, u32, u32, u32)>);
+
+ #[wasm_bindgen(js_name = many_arity_call1)]
+ fn many_arity_call_mut1(a: &Closure<dyn FnMut()>);
+ #[wasm_bindgen(js_name = many_arity_call2)]
+ fn many_arity_call_mut2(a: &Closure<dyn FnMut(u32)>);
+ #[wasm_bindgen(js_name = many_arity_call3)]
+ fn many_arity_call_mut3(a: &Closure<dyn FnMut(u32, u32)>);
+ #[wasm_bindgen(js_name = many_arity_call4)]
+ fn many_arity_call_mut4(a: &Closure<dyn FnMut(u32, u32, u32)>);
+ #[wasm_bindgen(js_name = many_arity_call5)]
+ fn many_arity_call_mut5(a: &Closure<dyn FnMut(u32, u32, u32, u32)>);
+ #[wasm_bindgen(js_name = many_arity_call6)]
+ fn many_arity_call_mut6(a: &Closure<dyn FnMut(u32, u32, u32, u32, u32)>);
+ #[wasm_bindgen(js_name = many_arity_call7)]
+ fn many_arity_call_mut7(a: &Closure<dyn FnMut(u32, u32, u32, u32, u32, u32)>);
+ #[wasm_bindgen(js_name = many_arity_call8)]
+ fn many_arity_call_mut8(a: &Closure<dyn FnMut(u32, u32, u32, u32, u32, u32, u32)>);
+ #[wasm_bindgen(js_name = many_arity_call9)]
+ fn many_arity_call_mut9(a: &Closure<dyn FnMut(u32, u32, u32, u32, u32, u32, u32, u32)>);
+
+ fn option_call1(a: Option<&Closure<dyn Fn()>>);
+ fn option_call2(a: Option<&Closure<dyn FnMut(u32) -> u32>>) -> u32;
+ fn option_call3(a: Option<&Closure<dyn Fn()>>) -> bool;
+
+ #[wasm_bindgen(js_name = many_arity_call1)]
+ fn many_arity_stack1(a: &dyn Fn());
+ #[wasm_bindgen(js_name = many_arity_call2)]
+ fn many_arity_stack2(a: &dyn Fn(u32));
+ #[wasm_bindgen(js_name = many_arity_call3)]
+ fn many_arity_stack3(a: &dyn Fn(u32, u32));
+ #[wasm_bindgen(js_name = many_arity_call4)]
+ fn many_arity_stack4(a: &dyn Fn(u32, u32, u32));
+ #[wasm_bindgen(js_name = many_arity_call5)]
+ fn many_arity_stack5(a: &dyn Fn(u32, u32, u32, u32));
+ #[wasm_bindgen(js_name = many_arity_call6)]
+ fn many_arity_stack6(a: &dyn Fn(u32, u32, u32, u32, u32));
+ #[wasm_bindgen(js_name = many_arity_call7)]
+ fn many_arity_stack7(a: &dyn Fn(u32, u32, u32, u32, u32, u32));
+ #[wasm_bindgen(js_name = many_arity_call8)]
+ fn many_arity_stack8(a: &dyn Fn(u32, u32, u32, u32, u32, u32, u32));
+ #[wasm_bindgen(js_name = many_arity_call9)]
+ fn many_arity_stack9(a: &dyn Fn(u32, u32, u32, u32, u32, u32, u32, u32));
+
+ fn long_lived_dropping_cache(a: &Closure<dyn Fn()>);
+ #[wasm_bindgen(catch)]
+ fn long_lived_dropping_call() -> Result<(), JsValue>;
+
+ fn long_lived_option_dropping_cache(a: Option<&Closure<dyn Fn()>>) -> bool;
+ #[wasm_bindgen(catch)]
+ fn long_lived_option_dropping_call() -> Result<(), JsValue>;
+
+ fn long_fnmut_recursive_cache(a: &Closure<dyn FnMut()>);
+ #[wasm_bindgen(catch)]
+ fn long_fnmut_recursive_call() -> Result<(), JsValue>;
+
+ fn fnmut_call(a: &mut dyn FnMut());
+ fn fnmut_thread(a: &mut dyn FnMut(u32) -> u32) -> u32;
+
+ fn fnmut_bad_call(a: &mut dyn FnMut());
+ #[wasm_bindgen(catch)]
+ fn fnmut_bad_again(a: bool) -> Result<(), JsValue>;
+
+ fn string_arguments_call(a: &mut dyn FnMut(String));
+
+ fn string_ret_call(a: &mut dyn FnMut(String) -> String);
+
+ fn drop_during_call_save(a: &Closure<dyn Fn()>);
+ fn drop_during_call_call();
+
+ fn js_test_closure_returner();
+
+ fn calling_it_throws(a: &Closure<dyn FnMut()>) -> bool;
+
+ fn call_val(f: &JsValue);
+
+ #[wasm_bindgen(js_name = calling_it_throws)]
+ fn call_val_throws(f: &JsValue) -> bool;
+
+ fn pass_reference_first_arg_twice(
+ a: RefFirstArgument,
+ b: &Closure<dyn FnMut(&RefFirstArgument)>,
+ c: &Closure<dyn FnMut(&RefFirstArgument)>,
+ );
+ #[wasm_bindgen(js_name = pass_reference_first_arg_twice)]
+ fn pass_reference_first_arg_twice2(
+ a: RefFirstArgument,
+ b: &mut dyn FnMut(&RefFirstArgument),
+ c: &mut dyn FnMut(&RefFirstArgument),
+ );
+ fn call_destroyed(a: &JsValue);
+
+ fn js_store_forgotten_closure(closure: &Closure<dyn Fn()>);
+ fn js_call_forgotten_closure();
+
+ #[wasm_bindgen(js_name = many_arity_call2)]
+ fn externref_call(a: &Closure<dyn Fn(JsValue)>);
+ #[wasm_bindgen(js_name = many_arity_call2)]
+ fn named_externref_call(a: &Closure<dyn Fn(Number)>);
+}
+
+#[wasm_bindgen_test]
+fn works() {
+ let a = Cell::new(false);
+ works_call(&|| a.set(true));
+ assert!(a.get());
+
+ assert_eq!(works_thread(&|a| a + 1), 3);
+}
+
+#[wasm_bindgen_test]
+fn cannot_reuse() {
+ cannot_reuse_call(&|| {});
+ assert!(cannot_reuse_call_again().is_err());
+}
+
+#[wasm_bindgen_test]
+fn debug() {
+ let closure = Closure::wrap(Box::new(|| {}) as Box<dyn FnMut()>);
+ assert_eq!(&format!("{:?}", closure), "Closure { ... }");
+}
+
+#[wasm_bindgen_test]
+fn long_lived() {
+ let hit = Rc::new(Cell::new(false));
+ let hit2 = hit.clone();
+ let a = Closure::new(move || hit2.set(true));
+ assert!(!hit.get());
+ long_lived_call1(&a);
+ assert!(hit.get());
+
+ let hit = Rc::new(Cell::new(false));
+ {
+ let hit = hit.clone();
+ let a = Closure::new(move |x| {
+ hit.set(true);
+ x + 3
+ });
+ assert_eq!(long_lived_call2(&a), 5);
+ }
+ assert!(hit.get());
+}
+
+#[wasm_bindgen_test]
+fn many_arity() {
+ many_arity_call1(&Closure::new(|| {}));
+ many_arity_call2(&Closure::new(|a| assert_eq!(a, 1)));
+ many_arity_call3(&Closure::new(|a, b| assert_eq!((a, b), (1, 2))));
+ many_arity_call4(&Closure::new(|a, b, c| assert_eq!((a, b, c), (1, 2, 3))));
+ many_arity_call5(&Closure::new(|a, b, c, d| {
+ assert_eq!((a, b, c, d), (1, 2, 3, 4))
+ }));
+ many_arity_call6(&Closure::new(|a, b, c, d, e| {
+ assert_eq!((a, b, c, d, e), (1, 2, 3, 4, 5))
+ }));
+ many_arity_call7(&Closure::new(|a, b, c, d, e, f| {
+ assert_eq!((a, b, c, d, e, f), (1, 2, 3, 4, 5, 6))
+ }));
+ many_arity_call8(&Closure::new(|a, b, c, d, e, f, g| {
+ assert_eq!((a, b, c, d, e, f, g), (1, 2, 3, 4, 5, 6, 7))
+ }));
+ many_arity_call9(&Closure::new(|a, b, c, d, e, f, g, h| {
+ assert_eq!((a, b, c, d, e, f, g, h), (1, 2, 3, 4, 5, 6, 7, 8))
+ }));
+
+ let s = String::new();
+ many_arity_call_mut1(&Closure::once(move || drop(s)));
+ let s = String::new();
+ many_arity_call_mut2(&Closure::once(move |a| {
+ drop(s);
+ assert_eq!(a, 1);
+ }));
+ let s = String::new();
+ many_arity_call_mut3(&Closure::once(move |a, b| {
+ drop(s);
+ assert_eq!((a, b), (1, 2));
+ }));
+ let s = String::new();
+ many_arity_call_mut4(&Closure::once(move |a, b, c| {
+ drop(s);
+ assert_eq!((a, b, c), (1, 2, 3));
+ }));
+ let s = String::new();
+ many_arity_call_mut5(&Closure::once(move |a, b, c, d| {
+ drop(s);
+ assert_eq!((a, b, c, d), (1, 2, 3, 4));
+ }));
+ let s = String::new();
+ many_arity_call_mut6(&Closure::once(move |a, b, c, d, e| {
+ drop(s);
+ assert_eq!((a, b, c, d, e), (1, 2, 3, 4, 5));
+ }));
+ let s = String::new();
+ many_arity_call_mut7(&Closure::once(move |a, b, c, d, e, f| {
+ drop(s);
+ assert_eq!((a, b, c, d, e, f), (1, 2, 3, 4, 5, 6));
+ }));
+ let s = String::new();
+ many_arity_call_mut8(&Closure::once(move |a, b, c, d, e, f, g| {
+ drop(s);
+ assert_eq!((a, b, c, d, e, f, g), (1, 2, 3, 4, 5, 6, 7));
+ }));
+ let s = String::new();
+ many_arity_call_mut9(&Closure::once(move |a, b, c, d, e, f, g, h| {
+ drop(s);
+ assert_eq!((a, b, c, d, e, f, g, h), (1, 2, 3, 4, 5, 6, 7, 8));
+ }));
+
+ many_arity_stack1(&(|| {}));
+ many_arity_stack2(&(|a| assert_eq!(a, 1)));
+ many_arity_stack3(&(|a, b| assert_eq!((a, b), (1, 2))));
+ many_arity_stack4(&(|a, b, c| assert_eq!((a, b, c), (1, 2, 3))));
+ many_arity_stack5(&(|a, b, c, d| assert_eq!((a, b, c, d), (1, 2, 3, 4))));
+ many_arity_stack6(&(|a, b, c, d, e| assert_eq!((a, b, c, d, e), (1, 2, 3, 4, 5))));
+ many_arity_stack7(&(|a, b, c, d, e, f| assert_eq!((a, b, c, d, e, f), (1, 2, 3, 4, 5, 6))));
+ many_arity_stack8(
+ &(|a, b, c, d, e, f, g| assert_eq!((a, b, c, d, e, f, g), (1, 2, 3, 4, 5, 6, 7))),
+ );
+ many_arity_stack9(
+ &(|a, b, c, d, e, f, g, h| assert_eq!((a, b, c, d, e, f, g, h), (1, 2, 3, 4, 5, 6, 7, 8))),
+ );
+}
+
+#[wasm_bindgen_test]
+fn option() {
+ let hit = Rc::new(Cell::new(false));
+ let hit2 = hit.clone();
+ let a = Closure::new(move || hit2.set(true));
+ assert!(!hit.get());
+ option_call1(Some(&a));
+ assert!(hit.get());
+
+ let hit = Rc::new(Cell::new(false));
+ {
+ let hit = hit.clone();
+ let a = Closure::new(move |x| {
+ hit.set(true);
+ x + 3
+ });
+ assert_eq!(option_call2(Some(&a)), 5);
+ }
+ assert!(hit.get());
+
+ assert!(option_call3(None));
+}
+
+struct Dropper(Rc<Cell<bool>>);
+impl Drop for Dropper {
+ fn drop(&mut self) {
+ assert!(!self.0.get());
+ self.0.set(true);
+ }
+}
+
+#[wasm_bindgen_test]
+fn call_fn_once_twice() {
+ let dropped = Rc::new(Cell::new(false));
+ let dropper = Dropper(dropped.clone());
+ let called = Rc::new(Cell::new(false));
+
+ let c = Closure::once({
+ let called = called.clone();
+ move || {
+ assert!(!called.get());
+ called.set(true);
+ drop(dropper);
+ }
+ });
+
+ many_arity_call_mut1(&c);
+ assert!(called.get());
+ assert!(dropped.get());
+
+ assert!(calling_it_throws(&c));
+}
+
+#[wasm_bindgen_test]
+fn once_into_js() {
+ let dropped = Rc::new(Cell::new(false));
+ let dropper = Dropper(dropped.clone());
+ let called = Rc::new(Cell::new(false));
+
+ let f = Closure::once_into_js({
+ let called = called.clone();
+ move || {
+ assert!(!called.get());
+ called.set(true);
+ drop(dropper);
+ }
+ });
+
+ call_val(&f);
+ assert!(called.get());
+ assert!(dropped.get());
+
+ assert!(call_val_throws(&f));
+}
+
+#[wasm_bindgen_test]
+fn long_lived_dropping() {
+ let hit = Rc::new(Cell::new(false));
+ let hit2 = hit.clone();
+ let a = Closure::new(move || hit2.set(true));
+ long_lived_dropping_cache(&a);
+ assert!(!hit.get());
+ assert!(long_lived_dropping_call().is_ok());
+ assert!(hit.get());
+ drop(a);
+ assert!(long_lived_dropping_call().is_err());
+}
+
+#[wasm_bindgen_test]
+fn long_lived_option_dropping() {
+ let hit = Rc::new(Cell::new(false));
+ let hit2 = hit.clone();
+
+ let a = Closure::new(move || hit2.set(true));
+
+ assert!(!long_lived_option_dropping_cache(None));
+ assert!(long_lived_option_dropping_cache(Some(&a)));
+
+ assert!(!hit.get());
+ assert!(long_lived_option_dropping_call().is_ok());
+ assert!(hit.get());
+
+ drop(a);
+ assert!(long_lived_option_dropping_call().is_err());
+}
+
+#[wasm_bindgen_test]
+fn long_fnmut_recursive() {
+ let a = Closure::new(|| {
+ assert!(long_fnmut_recursive_call().is_err());
+ });
+ long_fnmut_recursive_cache(&a);
+ assert!(long_fnmut_recursive_call().is_ok());
+}
+
+#[wasm_bindgen_test]
+fn fnmut() {
+ let mut a = false;
+ fnmut_call(&mut || a = true);
+ assert!(a);
+
+ let mut x = false;
+ assert_eq!(
+ fnmut_thread(&mut |a| {
+ x = true;
+ a + 1
+ }),
+ 3
+ );
+ assert!(x);
+}
+
+#[wasm_bindgen_test]
+fn fnmut_bad() {
+ let mut x = true;
+ let mut hits = 0;
+ fnmut_bad_call(&mut || {
+ hits += 1;
+ if fnmut_bad_again(hits == 1).is_err() {
+ return;
+ }
+ x = false;
+ });
+ assert_eq!(hits, 1);
+ assert!(x);
+
+ assert!(fnmut_bad_again(true).is_err());
+}
+
+#[wasm_bindgen_test]
+fn string_arguments() {
+ let mut x = false;
+ string_arguments_call(&mut |s| {
+ assert_eq!(s, "foo");
+ x = true;
+ });
+ assert!(x);
+}
+
+#[wasm_bindgen_test]
+fn string_ret() {
+ let mut x = false;
+ string_ret_call(&mut |mut s| {
+ assert_eq!(s, "foo");
+ s.push_str("bar");
+ x = true;
+ s
+ });
+ assert!(x);
+}
+
+#[wasm_bindgen_test]
+fn drop_drops() {
+ static mut HIT: bool = false;
+
+ struct A;
+
+ impl Drop for A {
+ fn drop(&mut self) {
+ unsafe {
+ HIT = true;
+ }
+ }
+ }
+ let a = A;
+ let x: Closure<dyn Fn()> = Closure::new(move || drop(&a));
+ drop(x);
+ unsafe {
+ assert!(HIT);
+ }
+}
+
+#[wasm_bindgen_test]
+fn drop_during_call_ok() {
+ static mut HIT: bool = false;
+ struct A;
+ impl Drop for A {
+ fn drop(&mut self) {
+ unsafe {
+ HIT = true;
+ }
+ }
+ }
+
+ let rc = Rc::new(RefCell::new(None));
+ let rc2 = rc.clone();
+ let x = 3;
+ let a = A;
+ let x: Closure<dyn Fn()> = Closure::new(move || {
+ // "drop ourselves"
+ drop(rc2.borrow_mut().take().unwrap());
+
+ // `A` should not have been destroyed as a result
+ unsafe {
+ assert!(!HIT);
+ }
+
+ // allocate some heap memory to try to paper over our `3`
+ drop(String::from("1234567890"));
+
+ // make sure our closure memory is still valid
+ assert_eq!(x, 3);
+
+ // make sure `A` is bound to our closure environment.
+ drop(&a);
+ unsafe {
+ assert!(!HIT);
+ }
+ });
+ drop_during_call_save(&x);
+ *rc.borrow_mut() = Some(x);
+ drop(rc);
+ unsafe {
+ assert!(!HIT);
+ }
+ drop_during_call_call();
+ unsafe {
+ assert!(HIT);
+ }
+}
+
+#[wasm_bindgen_test]
+fn test_closure_returner() {
+ type ClosureType = dyn FnMut() -> BadStruct;
+
+ use js_sys::{Object, Reflect};
+ use wasm_bindgen::JsCast;
+
+ js_test_closure_returner();
+
+ #[wasm_bindgen]
+ pub struct ClosureHandle(Closure<ClosureType>);
+
+ #[wasm_bindgen]
+ pub struct BadStruct {}
+
+ #[wasm_bindgen]
+ pub fn closure_returner() -> Result<Object, JsValue> {
+ let o = Object::new();
+
+ let some_fn = Closure::wrap(Box::new(move || BadStruct {}) as Box<ClosureType>);
+ Reflect::set(
+ &o,
+ &JsValue::from("someKey"),
+ &some_fn.as_ref().unchecked_ref(),
+ )
+ .unwrap();
+ Reflect::set(
+ &o,
+ &JsValue::from("handle"),
+ &JsValue::from(ClosureHandle(some_fn)),
+ )
+ .unwrap();
+
+ Ok(o)
+ }
+}
+
+#[wasm_bindgen]
+pub struct RefFirstArgument {
+ contents: u32,
+}
+
+#[wasm_bindgen_test]
+fn reference_as_first_argument_builds_at_all() {
+ #[wasm_bindgen]
+ extern "C" {
+ fn ref_first_arg1(a: &dyn Fn(&JsValue));
+ fn ref_first_arg2(a: &mut dyn FnMut(&JsValue));
+ fn ref_first_arg3(a: &Closure<dyn Fn(&JsValue)>);
+ fn ref_first_arg4(a: &Closure<dyn FnMut(&JsValue)>);
+ fn ref_first_custom1(a: &dyn Fn(&RefFirstArgument));
+ fn ref_first_custom2(a: &mut dyn FnMut(&RefFirstArgument));
+ fn ref_first_custom3(a: &Closure<dyn Fn(&RefFirstArgument)>);
+ fn ref_first_custom4(a: &Closure<dyn FnMut(&RefFirstArgument)>);
+ }
+
+ Closure::wrap(Box::new(|_: &JsValue| ()) as Box<dyn Fn(&JsValue)>);
+ Closure::wrap(Box::new(|_: &JsValue| ()) as Box<dyn FnMut(&JsValue)>);
+ Closure::once(|_: &JsValue| ());
+ Closure::once_into_js(|_: &JsValue| ());
+ Closure::wrap(Box::new(|_: &RefFirstArgument| ()) as Box<dyn Fn(&RefFirstArgument)>);
+ Closure::wrap(Box::new(|_: &RefFirstArgument| ()) as Box<dyn FnMut(&RefFirstArgument)>);
+ Closure::once(|_: &RefFirstArgument| ());
+ Closure::once_into_js(|_: &RefFirstArgument| ());
+}
+
+#[wasm_bindgen_test]
+fn reference_as_first_argument_works() {
+ let a = Rc::new(Cell::new(0));
+ let b = {
+ let a = a.clone();
+ Closure::once(move |x: &RefFirstArgument| {
+ assert_eq!(a.get(), 0);
+ assert_eq!(x.contents, 3);
+ a.set(a.get() + 1);
+ })
+ };
+ let c = {
+ let a = a.clone();
+ Closure::once(move |x: &RefFirstArgument| {
+ assert_eq!(a.get(), 1);
+ assert_eq!(x.contents, 3);
+ a.set(a.get() + 1);
+ })
+ };
+ pass_reference_first_arg_twice(RefFirstArgument { contents: 3 }, &b, &c);
+ assert_eq!(a.get(), 2);
+}
+
+#[wasm_bindgen_test]
+fn reference_as_first_argument_works2() {
+ let a = Cell::new(0);
+ pass_reference_first_arg_twice2(
+ RefFirstArgument { contents: 3 },
+ &mut |x: &RefFirstArgument| {
+ assert_eq!(a.get(), 0);
+ assert_eq!(x.contents, 3);
+ a.set(a.get() + 1);
+ },
+ &mut |x: &RefFirstArgument| {
+ assert_eq!(a.get(), 1);
+ assert_eq!(x.contents, 3);
+ a.set(a.get() + 1);
+ },
+ );
+ assert_eq!(a.get(), 2);
+}
+
+#[wasm_bindgen_test]
+fn call_destroyed_doesnt_segfault() {
+ struct A(i32, i32);
+ impl Drop for A {
+ fn drop(&mut self) {
+ assert_eq!(self.0, self.1);
+ }
+ }
+
+ let a = A(1, 1);
+ let a = Closure::wrap(Box::new(move || drop(&a)) as Box<dyn Fn()>);
+ let b = a.as_ref().clone();
+ drop(a);
+ call_destroyed(&b);
+
+ let a = A(2, 2);
+ let a = Closure::wrap(Box::new(move || drop(&a)) as Box<dyn FnMut()>);
+ let b = a.as_ref().clone();
+ drop(a);
+ call_destroyed(&b);
+
+ let a = A(1, 1);
+ let a = Closure::wrap(Box::new(move |_: &JsValue| drop(&a)) as Box<dyn Fn(&JsValue)>);
+ let b = a.as_ref().clone();
+ drop(a);
+ call_destroyed(&b);
+
+ let a = A(2, 2);
+ let a = Closure::wrap(Box::new(move |_: &JsValue| drop(&a)) as Box<dyn FnMut(&JsValue)>);
+ let b = a.as_ref().clone();
+ drop(a);
+ call_destroyed(&b);
+}
+
+#[wasm_bindgen_test]
+fn forget_works() {
+ let a = Closure::wrap(Box::new(|| {}) as Box<dyn Fn()>);
+ js_store_forgotten_closure(&a);
+ a.forget();
+ js_call_forgotten_closure();
+}
+
+#[wasm_bindgen_test]
+fn named_externref_no_duplicate_adapter() {
+ externref_call(&Closure::new(|a| assert_eq!(a, 1)));
+ named_externref_call(&Closure::new(|a| assert_eq!(a, 1)));
+}