diff options
Diffstat (limited to 'vendor/wasm-bindgen/tests/wasm/closures.rs')
-rw-r--r-- | vendor/wasm-bindgen/tests/wasm/closures.rs | 645 |
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))); +} |