diff --git a/src/closure.rs b/src/closure.rs index 04f688163b6..1b661f19c35 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -491,6 +491,15 @@ where } } +impl<'a, T> OptionIntoWasmAbi for &'a Closure +where + T: WasmClosure + ?Sized, +{ + fn none() -> Self::Abi { + 0 + } +} + fn _check() { fn _assert() {} _assert::<&Closure>(); diff --git a/tests/wasm/closures.js b/tests/wasm/closures.js index f14dcbfb170..dfc3d871e7d 100644 --- a/tests/wasm/closures.js +++ b/tests/wasm/closures.js @@ -51,6 +51,18 @@ exports.many_arity_call9 = a => { a(1, 2, 3, 4, 5, 6, 7, 8); }; +exports.option_call1 = a => { + if (a) { + a(); + } +}; +exports.option_call2 = a => { + if (a) { + return a(2); + } +}; +exports.option_call3 = a => a == undefined; + let LONG_LIVED_DROPPING_CACHE = null; exports.long_lived_dropping_cache = a => { @@ -60,6 +72,20 @@ exports.long_lived_dropping_call = () => { LONG_LIVED_DROPPING_CACHE(); }; +let LONG_LIVED_OPTION_DROPPING_CACHE = null; + +exports.long_lived_option_dropping_cache = a => { + if (a) { + LONG_LIVED_OPTION_DROPPING_CACHE = a; + return true; + } else { + return false; + } +} +exports.long_lived_option_dropping_call = () => { + LONG_LIVED_OPTION_DROPPING_CACHE(); +} + let LONG_FNMUT_RECURSIVE_CACHE = null; exports.long_fnmut_recursive_cache = a => { diff --git a/tests/wasm/closures.rs b/tests/wasm/closures.rs index d1dc6976744..4ac8ef0c11e 100644 --- a/tests/wasm/closures.rs +++ b/tests/wasm/closures.rs @@ -46,6 +46,10 @@ extern "C" { #[wasm_bindgen(js_name = many_arity_call9)] fn many_arity_call_mut9(a: &Closure); + fn option_call1(a: Option<&Closure>); + fn option_call2(a: Option<&Closure u32>>) -> u32; + fn option_call3(a: Option<&Closure>) -> bool; + #[wasm_bindgen(js_name = many_arity_call1)] fn many_arity_stack1(a: &dyn Fn()); #[wasm_bindgen(js_name = many_arity_call2)] @@ -69,6 +73,10 @@ extern "C" { #[wasm_bindgen(catch)] fn long_lived_dropping_call() -> Result<(), JsValue>; + fn long_lived_option_dropping_cache(a: Option<&Closure>) -> bool; + #[wasm_bindgen(catch)] + fn long_lived_option_dropping_call() -> Result<(), JsValue>; + fn long_fnmut_recursive_cache(a: &Closure); #[wasm_bindgen(catch)] fn long_fnmut_recursive_call() -> Result<(), JsValue>; @@ -235,6 +243,29 @@ fn many_arity() { ); } +#[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>); impl Drop for Dropper { fn drop(&mut self) { @@ -300,6 +331,24 @@ fn long_lived_dropping() { 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(|| {