mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-12-15 04:23:12 +03:00
68c5233f80
This commit starts the `wasm-bindgen` CLI tool down the road to being a true polyfill for WebIDL bindings. This refactor is probably the first of a few, but is hopefully the largest and most sprawling and everything will be a bit more targeted from here on out. The goal of this refactoring is to separate out the massive `crates/cli-support/src/js/mod.rs` into a number of separate pieces of functionality. It currently takes care of basically everything including: * Binding intrinsics * Handling anyref transformations * Generating all JS for imports/exports * All the logic for how to import and how to name imports * Execution and management of wasm-bindgen closures Many of these are separable concerns and most overlap with WebIDL bindings. The internal refactoring here is intended to make it more clear who's responsible for what as well as making some existing operations much more straightforward. At a high-level, the following changes are done: 1. A `src/webidl.rs` module is introduced. The purpose of this module is to take all of the raw wasm-bindgen custom sections from the module and transform them into a WebIDL bindings section. This module has a placeholder `WebidlCustomSection` which is nowhere near the actual custom section but if you squint is in theory very similar. It's hoped that this will eventually become the true WebIDL custom section, currently being developed in an external crate. Currently, however, the WebIDL bindings custom section only covers a subset of the functionality we export to wasm-bindgen users. To avoid leaving them high and dry this module also contains an auxiliary custom section named `WasmBindgenAux`. This custom section isn't intended to have a binary format, but is intended to represent a theoretical custom section necessary to couple with WebIDL bindings to achieve all our desired functionality in `wasm-bindgen`. It'll never be standardized, but it'll also never be serialized :) 2. The `src/webidl.rs` module now takes over quite a bit of functionality from `src/js/mod.rs`. Namely it handles synthesis of an `export_map` and an `import_map` mapping export/import IDs to exactly what's expected to be hooked up there. This does not include type information (as that's in the bindings section) but rather includes things like "this is the method of class A" or "this import is from module `foo`" and things like that. These could arguably be subsumed by future JS features as well, but that's for another time! 3. All handling of wasm-bindgen "descriptor functions" now happens in a dedicated `src/descriptors.rs` module. The output of this module is its own custom section (intended to be immediately consumed by the WebIDL module) which is in theory what we want to ourselves emit one day but rustc isn't capable of doing so right now. 4. Invocations and generations of imports are completely overhauled. Using the `import_map` generated in the WebIDL step all imports are now handled much more precisely in one location rather than haphazardly throughout the module. This means we have precise information about each import of the module and we only modify exactly what we're looking at. This also vastly simplifies intrinsic generation since it's all simply a codegen part of the `rust2js.rs` module now. 5. Handling of direct imports which don't have a JS shim generated is slightly different from before and is intended to be future-compatible with WebIDL bindings in its full glory, but we'll need to update it to handle cases for constructors and method calls eventually as well. 6. Intrinsic definitions now live in their own file (`src/intrinsic.rs`) and have a separated definition for their symbol name and signature. The actual implementation of each intrinsic lives in `rust2js.rs` There's a number of TODO items to finish before this merges. This includes reimplementing the anyref pass and actually implementing import maps for other targets. Those will come soon in follow-up commits, but the entire `tests/wasm/main.rs` suite is currently passing and this seems like a good checkpoint.
587 lines
16 KiB
Rust
Executable File
587 lines
16 KiB
Rust
Executable File
#![cfg(feature = "nightly")]
|
|
|
|
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: &Fn());
|
|
fn works_thread(a: &Fn(u32) -> u32) -> u32;
|
|
|
|
fn cannot_reuse_call(a: &Fn());
|
|
#[wasm_bindgen(catch)]
|
|
fn cannot_reuse_call_again() -> Result<(), JsValue>;
|
|
|
|
fn long_lived_call1(a: &Closure<Fn()>);
|
|
fn long_lived_call2(a: &Closure<FnMut(u32) -> u32>) -> u32;
|
|
|
|
fn many_arity_call1(a: &Closure<Fn()>);
|
|
fn many_arity_call2(a: &Closure<Fn(u32)>);
|
|
fn many_arity_call3(a: &Closure<Fn(u32, u32)>);
|
|
fn many_arity_call4(a: &Closure<Fn(u32, u32, u32)>);
|
|
fn many_arity_call5(a: &Closure<Fn(u32, u32, u32, u32)>);
|
|
fn many_arity_call6(a: &Closure<Fn(u32, u32, u32, u32, u32)>);
|
|
fn many_arity_call7(a: &Closure<Fn(u32, u32, u32, u32, u32, u32)>);
|
|
fn many_arity_call8(a: &Closure<Fn(u32, u32, u32, u32, u32, u32, u32)>);
|
|
fn many_arity_call9(a: &Closure<Fn(u32, u32, u32, u32, u32, u32, u32, u32)>);
|
|
|
|
#[wasm_bindgen(js_name = many_arity_call1)]
|
|
fn many_arity_call_mut1(a: &Closure<FnMut()>);
|
|
#[wasm_bindgen(js_name = many_arity_call2)]
|
|
fn many_arity_call_mut2(a: &Closure<FnMut(u32)>);
|
|
#[wasm_bindgen(js_name = many_arity_call3)]
|
|
fn many_arity_call_mut3(a: &Closure<FnMut(u32, u32)>);
|
|
#[wasm_bindgen(js_name = many_arity_call4)]
|
|
fn many_arity_call_mut4(a: &Closure<FnMut(u32, u32, u32)>);
|
|
#[wasm_bindgen(js_name = many_arity_call5)]
|
|
fn many_arity_call_mut5(a: &Closure<FnMut(u32, u32, u32, u32)>);
|
|
#[wasm_bindgen(js_name = many_arity_call6)]
|
|
fn many_arity_call_mut6(a: &Closure<FnMut(u32, u32, u32, u32, u32)>);
|
|
#[wasm_bindgen(js_name = many_arity_call7)]
|
|
fn many_arity_call_mut7(a: &Closure<FnMut(u32, u32, u32, u32, u32, u32)>);
|
|
#[wasm_bindgen(js_name = many_arity_call8)]
|
|
fn many_arity_call_mut8(a: &Closure<FnMut(u32, u32, u32, u32, u32, u32, u32)>);
|
|
#[wasm_bindgen(js_name = many_arity_call9)]
|
|
fn many_arity_call_mut9(a: &Closure<FnMut(u32, u32, u32, u32, u32, u32, u32, u32)>);
|
|
|
|
#[wasm_bindgen(js_name = many_arity_call1)]
|
|
fn many_arity_stack1(a: &Fn());
|
|
#[wasm_bindgen(js_name = many_arity_call2)]
|
|
fn many_arity_stack2(a: &Fn(u32));
|
|
#[wasm_bindgen(js_name = many_arity_call3)]
|
|
fn many_arity_stack3(a: &Fn(u32, u32));
|
|
#[wasm_bindgen(js_name = many_arity_call4)]
|
|
fn many_arity_stack4(a: &Fn(u32, u32, u32));
|
|
#[wasm_bindgen(js_name = many_arity_call5)]
|
|
fn many_arity_stack5(a: &Fn(u32, u32, u32, u32));
|
|
#[wasm_bindgen(js_name = many_arity_call6)]
|
|
fn many_arity_stack6(a: &Fn(u32, u32, u32, u32, u32));
|
|
#[wasm_bindgen(js_name = many_arity_call7)]
|
|
fn many_arity_stack7(a: &Fn(u32, u32, u32, u32, u32, u32));
|
|
#[wasm_bindgen(js_name = many_arity_call8)]
|
|
fn many_arity_stack8(a: &Fn(u32, u32, u32, u32, u32, u32, u32));
|
|
#[wasm_bindgen(js_name = many_arity_call9)]
|
|
fn many_arity_stack9(a: &Fn(u32, u32, u32, u32, u32, u32, u32, u32));
|
|
|
|
fn long_lived_dropping_cache(a: &Closure<Fn()>);
|
|
#[wasm_bindgen(catch)]
|
|
fn long_lived_dropping_call() -> Result<(), JsValue>;
|
|
|
|
fn long_fnmut_recursive_cache(a: &Closure<FnMut()>);
|
|
#[wasm_bindgen(catch)]
|
|
fn long_fnmut_recursive_call() -> Result<(), JsValue>;
|
|
|
|
fn fnmut_call(a: &mut FnMut());
|
|
fn fnmut_thread(a: &mut FnMut(u32) -> u32) -> u32;
|
|
|
|
fn fnmut_bad_call(a: &mut FnMut());
|
|
#[wasm_bindgen(catch)]
|
|
fn fnmut_bad_again(a: bool) -> Result<(), JsValue>;
|
|
|
|
fn string_arguments_call(a: &mut FnMut(String));
|
|
|
|
fn string_ret_call(a: &mut FnMut(String) -> String);
|
|
|
|
fn drop_during_call_save(a: &Closure<Fn()>);
|
|
fn drop_during_call_call();
|
|
|
|
fn js_test_closure_returner();
|
|
|
|
fn calling_it_throws(a: &Closure<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<FnMut(&RefFirstArgument)>,
|
|
c: &Closure<FnMut(&RefFirstArgument)>,
|
|
);
|
|
#[wasm_bindgen(js_name = pass_reference_first_arg_twice)]
|
|
fn pass_reference_first_arg_twice2(
|
|
a: RefFirstArgument,
|
|
b: &mut FnMut(&RefFirstArgument),
|
|
c: &mut FnMut(&RefFirstArgument),
|
|
);
|
|
fn call_destroyed(a: &JsValue);
|
|
|
|
fn js_store_forgotten_closure(closure: &Closure<Fn()>);
|
|
fn js_call_forgotten_closure();
|
|
}
|
|
|
|
#[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<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))),
|
|
);
|
|
}
|
|
|
|
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_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<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<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 = 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: &Fn(&JsValue));
|
|
fn ref_first_arg2(a: &mut FnMut(&JsValue));
|
|
fn ref_first_arg3(a: &Closure<Fn(&JsValue)>);
|
|
fn ref_first_arg4(a: &Closure<FnMut(&JsValue)>);
|
|
fn ref_first_custom1(a: &Fn(&RefFirstArgument));
|
|
fn ref_first_custom2(a: &mut FnMut(&RefFirstArgument));
|
|
fn ref_first_custom3(a: &Closure<Fn(&RefFirstArgument)>);
|
|
fn ref_first_custom4(a: &Closure<FnMut(&RefFirstArgument)>);
|
|
}
|
|
|
|
Closure::wrap(Box::new(|_: &JsValue| ()) as Box<Fn(&JsValue)>);
|
|
Closure::wrap(Box::new(|_: &JsValue| ()) as Box<FnMut(&JsValue)>);
|
|
Closure::once(|_: &JsValue| ());
|
|
Closure::once_into_js(|_: &JsValue| ());
|
|
Closure::wrap(Box::new(|_: &RefFirstArgument| ()) as Box<Fn(&RefFirstArgument)>);
|
|
Closure::wrap(Box::new(|_: &RefFirstArgument| ()) as Box<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<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<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<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<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<Fn()>);
|
|
js_store_forgotten_closure(&a);
|
|
a.forget();
|
|
js_call_forgotten_closure();
|
|
}
|