mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-12 20:41:24 +00:00
Add tests for the interface types output of wasm-bindgen (#1898)
* Add tests for the interface types output of wasm-bindgen This commit expands the test suite with assertions about the output of the interface types pass in wasm-bindgen. The goal here is to actually assert that we produce the right output and have a suite of reference files to show how the interface types output is changing over time. The `reference` test suite added in the previous PR has been updated to work for interface types as well, generating `*.wit` file assertions which are printed via the `wit-printer` crate on crates.io. Along the way a number of bugs were fixed with the interface types output, such as: * Non-determinism in output caused by iteration of a `HashMap` * Avoiding JS generation entirely in interface types mode, ensuring that we don't export extraneous intrinsics that aren't otherwise needed. * Fixing location of the stack pointer for modules where it's GC'd out. It's now rooted in the aux section of wasm-bindgen so it's available to later passes, like the multi-value pass. * Interface types emission now works in debug mode, meaning the `--release` flag is no longer required. This previously did not work because the `__wbindgen_throw` intrinsic was required in debug mode. This comes about because of the `malloc_failure` and `internal_error` functions in the anyref pass. The purpose of these functions is to signal fatal runtime errors, if any, in a way that's usable to the user. For wasm interface types though we can replace calls to these functions with `unreachable` to avoid needing to import the intrinsic. This has the accidental side effect of making `wasm_bindgen::throw_str` "just work" with wasm interface types by aborting the program, but that's not actually entirely intended. It's hoped that a split of a `wasm-bindgen-core` crate would solve this issue for the future. * Run the wasm interface types validator in tests * Add more gc roots for adapter gc * Improve stack pointer detection The stack pointer is never initialized to zero, but some other mutable globals are (TLS, thread ID, etc), so let's filter those out.
This commit is contained in:
@ -74,17 +74,18 @@ fn runtest(test: &Path) -> Result<()> {
|
||||
repo_root().display(),
|
||||
test.display(),
|
||||
);
|
||||
let interface_types = contents.contains("// interface-types");
|
||||
|
||||
fs::write(td.path().join("Cargo.toml"), manifest)?;
|
||||
let target_dir = target_dir();
|
||||
exec(
|
||||
Command::new("cargo")
|
||||
.current_dir(td.path())
|
||||
.arg("build")
|
||||
.arg("--target")
|
||||
.arg("wasm32-unknown-unknown")
|
||||
.env("CARGO_TARGET_DIR", &target_dir),
|
||||
)?;
|
||||
let mut cargo = Command::new("cargo");
|
||||
cargo
|
||||
.current_dir(td.path())
|
||||
.arg("build")
|
||||
.arg("--target")
|
||||
.arg("wasm32-unknown-unknown")
|
||||
.env("CARGO_TARGET_DIR", &target_dir);
|
||||
exec(&mut cargo)?;
|
||||
|
||||
let wasm = target_dir
|
||||
.join("wasm32-unknown-unknown")
|
||||
@ -100,26 +101,33 @@ fn runtest(test: &Path) -> Result<()> {
|
||||
if contents.contains("// enable-anyref") {
|
||||
bindgen.env("WASM_BINDGEN_ANYREF", "1");
|
||||
}
|
||||
if interface_types {
|
||||
bindgen.env("WASM_INTERFACE_TYPES", "1");
|
||||
}
|
||||
exec(&mut bindgen)?;
|
||||
|
||||
let js = fs::read_to_string(td.path().join("reference_test.js"))?;
|
||||
let wat = sanitize_wasm(&td.path().join("reference_test_bg.wasm"))?;
|
||||
|
||||
let js_assertion = test.with_extension("js");
|
||||
let wat_assertion = test.with_extension("wat");
|
||||
|
||||
if env::var("BLESS").is_ok() {
|
||||
fs::write(js_assertion, js)?;
|
||||
fs::write(wat_assertion, wat)?;
|
||||
return Ok(());
|
||||
if interface_types {
|
||||
let wasm = td.path().join("reference_test.wasm");
|
||||
wit_validator::validate(&fs::read(&wasm)?)?;
|
||||
let wit = sanitize_wasm(&wasm)?;
|
||||
assert_same(&wit, &test.with_extension("wit"))?;
|
||||
} else {
|
||||
let js = fs::read_to_string(td.path().join("reference_test.js"))?;
|
||||
assert_same(&js, &test.with_extension("js"))?;
|
||||
let wat = sanitize_wasm(&td.path().join("reference_test_bg.wasm"))?;
|
||||
assert_same(&wat, &test.with_extension("wat"))?;
|
||||
}
|
||||
|
||||
let js_expected = fs::read_to_string(&js_assertion)?;
|
||||
let wat_expected = fs::read_to_string(&wat_assertion)?;
|
||||
|
||||
diff(&js_expected, &js)?;
|
||||
diff(&wat_expected, &wat)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assert_same(output: &str, expected: &Path) -> Result<()> {
|
||||
if env::var("BLESS").is_ok() {
|
||||
fs::write(expected, output)?;
|
||||
} else {
|
||||
let expected = fs::read_to_string(&expected)?;
|
||||
diff(&expected, output)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -127,7 +135,9 @@ fn sanitize_wasm(wasm: &Path) -> Result<String> {
|
||||
// Clean up the wasm module by removing all function
|
||||
// implementations/instructions, data sections, etc. This'll help us largely
|
||||
// only deal with exports/imports which is all we're really interested in.
|
||||
let mut module = walrus::Module::from_file(wasm)?;
|
||||
let mut module = walrus::ModuleConfig::new()
|
||||
.on_parse(wit_walrus::on_parse)
|
||||
.parse_file(wasm)?;
|
||||
for func in module.funcs.iter_mut() {
|
||||
let local = match &mut func.kind {
|
||||
walrus::FunctionKind::Local(l) => l,
|
||||
@ -155,7 +165,7 @@ fn sanitize_wasm(wasm: &Path) -> Result<String> {
|
||||
module.exports.delete(id);
|
||||
}
|
||||
walrus::passes::gc::run(&mut module);
|
||||
let mut wat = wasmprinter::print_bytes(&module.emit_wasm())?;
|
||||
let mut wat = wit_printer::print_bytes(&module.emit_wasm())?;
|
||||
wat.push_str("\n");
|
||||
Ok(wat)
|
||||
}
|
||||
|
8
crates/cli/tests/reference/interface-types-anyref.rs
Normal file
8
crates/cli/tests/reference/interface-types-anyref.rs
Normal file
@ -0,0 +1,8 @@
|
||||
// interface-types
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn anyref_in_out(a: &JsValue, b: JsValue) -> JsValue {
|
||||
b
|
||||
}
|
12
crates/cli/tests/reference/interface-types-anyref.wit
Normal file
12
crates/cli/tests/reference/interface-types-anyref.wit
Normal file
@ -0,0 +1,12 @@
|
||||
(module
|
||||
(type (;0;) (func (param anyref anyref) (result anyref)))
|
||||
(func $anyref_in_out anyref shim (type 0) (param anyref anyref) (result anyref))
|
||||
(memory (;0;) 17)
|
||||
(export "memory" (memory 0))
|
||||
(export "anyref_in_out" (func $anyref_in_out anyref shim))
|
||||
(@interface type (;0;) (func (param anyref) (param anyref) (result anyref)))
|
||||
(@interface func (;0;) (type 0)
|
||||
arg.get 0
|
||||
arg.get 1
|
||||
call-core $anyref_in_out anyref shim)
|
||||
(@interface export "anyref_in_out" (func 0)))
|
7
crates/cli/tests/reference/interface-types-empty.rs
Normal file
7
crates/cli/tests/reference/interface-types-empty.rs
Normal file
@ -0,0 +1,7 @@
|
||||
// interface-types
|
||||
//
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn empty() {}
|
||||
|
10
crates/cli/tests/reference/interface-types-empty.wit
Normal file
10
crates/cli/tests/reference/interface-types-empty.wit
Normal file
@ -0,0 +1,10 @@
|
||||
(module
|
||||
(type (;0;) (func))
|
||||
(func $empty (type 0))
|
||||
(memory (;0;) 17)
|
||||
(export "memory" (memory 0))
|
||||
(export "empty" (func $empty))
|
||||
(@interface type (;0;) (func))
|
||||
(@interface func (;0;) (type 0)
|
||||
call-core $empty)
|
||||
(@interface export "empty" (func 0)))
|
46
crates/cli/tests/reference/interface-types-integers.rs
Normal file
46
crates/cli/tests/reference/interface-types-integers.rs
Normal file
@ -0,0 +1,46 @@
|
||||
// interface-types
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn integers(_a1: u8, _a2: i8, _a3: u16, _a4: i16, _a5: u32, _a6: i32, _a7: f32, _a8: f64) {}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn ret_i8() -> i8 {
|
||||
0
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn ret_u8() -> u8 {
|
||||
1
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn ret_i16() -> i16 {
|
||||
2
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn ret_u16() -> u16 {
|
||||
3
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn ret_i32() -> i32 {
|
||||
4
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn ret_u32() -> u32 {
|
||||
5
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn ret_f32() -> f32 {
|
||||
6.0
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn ret_f64() -> f64 {
|
||||
7.0
|
||||
}
|
81
crates/cli/tests/reference/interface-types-integers.wit
Normal file
81
crates/cli/tests/reference/interface-types-integers.wit
Normal file
@ -0,0 +1,81 @@
|
||||
(module
|
||||
(type (;0;) (func (result i32)))
|
||||
(type (;1;) (func (result f32)))
|
||||
(type (;2;) (func (result f64)))
|
||||
(type (;3;) (func (param i32 i32 i32 i32 i32 i32 f32 f64)))
|
||||
(func $integers (type 3) (param i32 i32 i32 i32 i32 i32 f32 f64))
|
||||
(func $ret_i8 (type 0) (result i32))
|
||||
(func $ret_u8 (type 0) (result i32))
|
||||
(func $ret_i16 (type 0) (result i32))
|
||||
(func $ret_u16 (type 0) (result i32))
|
||||
(func $ret_i32 (type 0) (result i32))
|
||||
(func $ret_u32 (type 0) (result i32))
|
||||
(func $ret_f32 (type 1) (result f32))
|
||||
(func $ret_f64 (type 2) (result f64))
|
||||
(memory (;0;) 17)
|
||||
(export "memory" (memory 0))
|
||||
(export "integers" (func $integers))
|
||||
(export "ret_i8" (func $ret_i8))
|
||||
(export "ret_u8" (func $ret_u8))
|
||||
(export "ret_i16" (func $ret_i16))
|
||||
(export "ret_u16" (func $ret_u16))
|
||||
(export "ret_i32" (func $ret_i32))
|
||||
(export "ret_u32" (func $ret_u32))
|
||||
(export "ret_f32" (func $ret_f32))
|
||||
(export "ret_f64" (func $ret_f64))
|
||||
(@interface type (;0;) (func (param u8) (param s8) (param u16) (param s16) (param u32) (param s32) (param f32) (param f64)))
|
||||
(@interface type (;1;) (func (result s8)))
|
||||
(@interface type (;2;) (func (result u8)))
|
||||
(@interface type (;3;) (func (result s16)))
|
||||
(@interface type (;4;) (func (result u16)))
|
||||
(@interface type (;5;) (func (result s32)))
|
||||
(@interface type (;6;) (func (result u32)))
|
||||
(@interface type (;7;) (func (result f32)))
|
||||
(@interface type (;8;) (func (result f64)))
|
||||
(@interface func (;0;) (type 0)
|
||||
arg.get 0
|
||||
u8-to-i32
|
||||
arg.get 1
|
||||
s8-to-i32
|
||||
arg.get 2
|
||||
u16-to-i32
|
||||
arg.get 3
|
||||
s16-to-i32
|
||||
arg.get 4
|
||||
u32-to-i32
|
||||
arg.get 5
|
||||
s32-to-i32
|
||||
arg.get 6
|
||||
arg.get 7
|
||||
call-core $integers)
|
||||
(@interface func (;1;) (type 1)
|
||||
call-core $ret_i8
|
||||
i32-to-s8)
|
||||
(@interface func (;2;) (type 2)
|
||||
call-core $ret_u8
|
||||
i32-to-u8)
|
||||
(@interface func (;3;) (type 3)
|
||||
call-core $ret_i16
|
||||
i32-to-s16)
|
||||
(@interface func (;4;) (type 4)
|
||||
call-core $ret_u16
|
||||
i32-to-u16)
|
||||
(@interface func (;5;) (type 5)
|
||||
call-core $ret_i32
|
||||
i32-to-s32)
|
||||
(@interface func (;6;) (type 6)
|
||||
call-core $ret_u32
|
||||
i32-to-u32)
|
||||
(@interface func (;7;) (type 7)
|
||||
call-core $ret_f32)
|
||||
(@interface func (;8;) (type 8)
|
||||
call-core $ret_f64)
|
||||
(@interface export "integers" (func 0))
|
||||
(@interface export "ret_i8" (func 1))
|
||||
(@interface export "ret_u8" (func 2))
|
||||
(@interface export "ret_i16" (func 3))
|
||||
(@interface export "ret_u16" (func 4))
|
||||
(@interface export "ret_i32" (func 5))
|
||||
(@interface export "ret_u32" (func 6))
|
||||
(@interface export "ret_f32" (func 7))
|
||||
(@interface export "ret_f64" (func 8)))
|
8
crates/cli/tests/reference/interface-types-interop.rs
Normal file
8
crates/cli/tests/reference/interface-types-interop.rs
Normal file
@ -0,0 +1,8 @@
|
||||
// interface-types
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn take_and_return(a: u8) -> u16 {
|
||||
a.into()
|
||||
}
|
13
crates/cli/tests/reference/interface-types-interop.wit
Normal file
13
crates/cli/tests/reference/interface-types-interop.wit
Normal file
@ -0,0 +1,13 @@
|
||||
(module
|
||||
(type (;0;) (func (param i32) (result i32)))
|
||||
(func $take_and_return (type 0) (param i32) (result i32))
|
||||
(memory (;0;) 17)
|
||||
(export "memory" (memory 0))
|
||||
(export "take_and_return" (func $take_and_return))
|
||||
(@interface type (;0;) (func (param u8) (result u16)))
|
||||
(@interface func (;0;) (type 0)
|
||||
arg.get 0
|
||||
u8-to-i32
|
||||
call-core $take_and_return
|
||||
i32-to-u16)
|
||||
(@interface export "take_and_return" (func 0)))
|
11
crates/cli/tests/reference/interface-types-strings.rs
Normal file
11
crates/cli/tests/reference/interface-types-strings.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// interface-types
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn strings(a: &str) -> String {
|
||||
String::new()
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn many_strings(a: &str, b: String) {}
|
29
crates/cli/tests/reference/interface-types-strings.wit
Normal file
29
crates/cli/tests/reference/interface-types-strings.wit
Normal file
@ -0,0 +1,29 @@
|
||||
(module
|
||||
(type (;0;) (func (param i32) (result i32)))
|
||||
(type (;1;) (func (param i32 i32)))
|
||||
(type (;2;) (func (param i32 i32) (result i32 i32)))
|
||||
(type (;3;) (func (param i32 i32 i32 i32)))
|
||||
(func $__wbindgen_malloc (type 0) (param i32) (result i32))
|
||||
(func $many_strings (type 3) (param i32 i32 i32 i32))
|
||||
(func $__wbindgen_free (type 1) (param i32 i32))
|
||||
(func $strings multivalue shim (type 2) (param i32 i32) (result i32 i32))
|
||||
(memory (;0;) 17)
|
||||
(export "memory" (memory 0))
|
||||
(export "strings" (func $strings multivalue shim))
|
||||
(export "many_strings" (func $many_strings))
|
||||
(@interface type (;0;) (func (param string) (result string)))
|
||||
(@interface type (;1;) (func (param string) (param string)))
|
||||
(@interface func (;0;) (type 0)
|
||||
arg.get 0
|
||||
string-to-memory $__wbindgen_malloc
|
||||
call-core $strings multivalue shim
|
||||
defer-call-core $__wbindgen_free
|
||||
memory-to-string)
|
||||
(@interface func (;1;) (type 1)
|
||||
arg.get 0
|
||||
string-to-memory $__wbindgen_malloc
|
||||
arg.get 1
|
||||
string-to-memory $__wbindgen_malloc
|
||||
call-core $many_strings)
|
||||
(@interface export "strings" (func 0))
|
||||
(@interface export "many_strings" (func 1)))
|
@ -233,3 +233,104 @@ fn empty_interface_types() {
|
||||
cmd.env("WASM_INTERFACE_TYPES", "1");
|
||||
cmd.assert().success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_interface_types_export() -> anyhow::Result<()> {
|
||||
let (mut cmd, _out_dir) = Project::new("bad_interface_types_export")
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
r#"
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn foo(a: Vec<u8>) {}
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
&format!(
|
||||
"
|
||||
[package]
|
||||
name = \"bad_interface_types_export\"
|
||||
authors = []
|
||||
version = \"1.0.0\"
|
||||
edition = '2018'
|
||||
|
||||
[lib]
|
||||
crate-type = [\"cdylib\"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = {{ path = '{}' }}
|
||||
|
||||
[workspace]
|
||||
",
|
||||
repo_root().display(),
|
||||
),
|
||||
)
|
||||
.wasm_bindgen("");
|
||||
cmd.env("WASM_INTERFACE_TYPES", "1");
|
||||
cmd.assert().failure().code(1).stderr(str::is_match(
|
||||
"\
|
||||
error: failed to generate a standard interface types section
|
||||
|
||||
Caused by:
|
||||
0: in function export `foo`
|
||||
1: type Vector\\(U8\\) isn't supported in standard interface types
|
||||
$",
|
||||
)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_interface_types_import() -> anyhow::Result<()> {
|
||||
let (mut cmd, _out_dir) = Project::new("bad_interface_types_import")
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
r#"
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
pub fn foo() -> Vec<u8>;
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn bar() {
|
||||
foo();
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
&format!(
|
||||
"
|
||||
[package]
|
||||
name = \"bad_interface_types_import\"
|
||||
authors = []
|
||||
version = \"1.0.0\"
|
||||
edition = '2018'
|
||||
|
||||
[lib]
|
||||
crate-type = [\"cdylib\"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = {{ path = '{}' }}
|
||||
|
||||
[workspace]
|
||||
",
|
||||
repo_root().display(),
|
||||
),
|
||||
)
|
||||
.wasm_bindgen("");
|
||||
cmd.env("WASM_INTERFACE_TYPES", "1");
|
||||
cmd.assert().failure().code(1).stderr(str::is_match(
|
||||
"\
|
||||
error: failed to generate a standard interface types section
|
||||
|
||||
Caused by:
|
||||
0: in adapter function
|
||||
1: import of global `foo` requires JS glue
|
||||
$",
|
||||
)?);
|
||||
Ok(())
|
||||
}
|
||||
|
Reference in New Issue
Block a user