Skip to content

Commit 057c915

Browse files
authored
Add test for consuming interface types inputs (#1900)
This commit adds a test suite for consuming interface types modules as input and producing a JS polyfill output. The tests are relatively simple today and don't exercise a ton of functionality, but they should hopefully cover the breadth of at least some basics of what wasm interface types supports today. A few small fixes were applied along the way, such as: * Don't require modules to have a stack pointer * Allow passing `*.wat`, `*.wit`, or `*.wasm` files as input to `wasm-bindgen` instead of always requiring `*.wasm`.
1 parent a1d9039 commit 057c915

22 files changed

+319
-15
lines changed

azure-pipelines.yml

+3
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ jobs:
9292
# - template: ci/azure-install-sccache.yml
9393
- script: rustup target add wasm32-unknown-unknown
9494
displayName: "install wasm target"
95+
- task: NodeTool@0
96+
inputs:
97+
versionSpec: '>=13.0'
9598
- script: cargo test -p wasm-bindgen-cli-support
9699
displayName: "wasm-bindgen-cli-support tests"
97100
- script: cargo test -p wasm-bindgen-cli

crates/cli-support/Cargo.toml

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,19 @@ Shared support for the wasm-bindgen-cli package, an internal dependency
1212
edition = '2018'
1313

1414
[dependencies]
15-
base64 = "0.9"
1615
anyhow = "1.0"
16+
base64 = "0.9"
1717
log = "0.4"
1818
rustc-demangle = "0.1.13"
1919
serde_json = "1.0"
2020
tempfile = "3.0"
2121
walrus = "0.14.0"
2222
wasm-bindgen-anyref-xform = { path = '../anyref-xform', version = '=0.2.55' }
23-
wasm-bindgen-shared = { path = "../shared", version = '=0.2.55' }
2423
wasm-bindgen-multi-value-xform = { path = '../multi-value-xform', version = '=0.2.55' }
24+
wasm-bindgen-shared = { path = "../shared", version = '=0.2.55' }
2525
wasm-bindgen-threads-xform = { path = '../threads-xform', version = '=0.2.55' }
2626
wasm-bindgen-wasm-conventions = { path = '../wasm-conventions', version = '=0.2.55' }
2727
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.55' }
28+
wit-text = "0.1.1"
2829
wit-walrus = "0.1.0"
30+
wit-validator = "0.1.0"

crates/cli-support/src/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,10 @@ impl Bindgen {
264264
(mem::replace(m, blank_module), &name[..])
265265
}
266266
Input::Path(ref path) => {
267-
let contents = fs::read(&path)
267+
let wasm = wit_text::parse_file(&path)
268268
.with_context(|| format!("failed to read `{}`", path.display()))?;
269+
wit_validator::validate(&wasm)
270+
.with_context(|| format!("failed to validate `{}`", path.display()))?;
269271
let module = walrus::ModuleConfig::new()
270272
// Skip validation of the module as LLVM's output is
271273
// generally already well-formed and so we won't gain much
@@ -278,7 +280,7 @@ impl Bindgen {
278280
.generate_name_section(!self.remove_name_section)
279281
.generate_producers_section(!self.remove_producers_section)
280282
.on_parse(wit_walrus::on_parse)
281-
.parse(&contents)
283+
.parse(&wasm)
282284
.context("failed to parse input file as wasm")?;
283285
let stem = match &self.out_name {
284286
Some(name) => &name,

crates/cli-support/src/wit/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ pub fn process(
9292

9393
impl<'a> Context<'a> {
9494
fn init(&mut self) -> Result<(), Error> {
95-
let stack_pointer = wasm_bindgen_wasm_conventions::get_shadow_stack_pointer(self.module)?;
96-
self.aux.shadow_stack_pointer = Some(stack_pointer);
95+
self.aux.shadow_stack_pointer =
96+
wasm_bindgen_wasm_conventions::get_shadow_stack_pointer(self.module);
9797

9898
// Make a map from string name to ids of all exports
9999
for export in self.module.exports.iter() {

crates/cli/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,17 @@ rayon = "1.0"
3636
tempfile = "3.0"
3737
walrus = "0.14"
3838
wit-printer = "0.1"
39+
wit-text = "0.1"
3940
wit-validator = "0.1"
4041
wit-walrus = "0.1"
4142

4243
[[test]]
4344
name = "reference"
4445
harness = false
4546

47+
[[test]]
48+
name = "interface-types"
49+
harness = false
50+
4651
[features]
4752
vendored-openssl = ['openssl/vendored']

crates/cli/tests/interface-types.rs

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use anyhow::{bail, Result};
2+
use assert_cmd::prelude::*;
3+
use rayon::prelude::*;
4+
use std::env;
5+
use std::path::Path;
6+
use std::process::Command;
7+
8+
fn main() -> Result<()> {
9+
let filter = env::args().nth(1);
10+
11+
let mut tests = Vec::new();
12+
let dir = env::current_dir()?.join("tests/interface-types");
13+
for entry in dir.read_dir()? {
14+
let path = entry?.path();
15+
if path.extension().and_then(|s| s.to_str()) != Some("wit") {
16+
continue;
17+
}
18+
if let Some(filter) = &filter {
19+
if !path.display().to_string().contains(filter) {
20+
continue;
21+
}
22+
}
23+
tests.push(path);
24+
}
25+
tests.sort();
26+
27+
let errs = tests
28+
.par_iter()
29+
.filter_map(|t| runtest(t).err().map(|e| (t, e)))
30+
.collect::<Vec<_>>();
31+
32+
if errs.len() == 0 {
33+
println!("{} tests passed", tests.len());
34+
return Ok(());
35+
}
36+
eprintln!("failed tests:\n");
37+
for (test, err) in errs {
38+
eprintln!("{} failure\n{}", test.display(), tab(&format!("{:?}", err)));
39+
}
40+
bail!("tests failed");
41+
}
42+
43+
fn runtest(test: &Path) -> Result<()> {
44+
let js = test.with_extension("js");
45+
let td = tempfile::TempDir::new()?;
46+
47+
let mut bindgen = Command::cargo_bin("wasm-bindgen")?;
48+
bindgen
49+
.arg("--out-dir")
50+
.arg(td.path())
51+
.arg(test)
52+
.arg("--out-name=wasm")
53+
.arg("--nodejs")
54+
.arg("--no-typescript");
55+
exec(&mut bindgen)?;
56+
57+
exec(Command::new("node")
58+
.arg("--experimental-wasm-anyref")
59+
.arg("--experimental-wasm-mv")
60+
.arg(&js).env("NODE_PATH", td.path()))?;
61+
62+
Ok(())
63+
}
64+
65+
fn exec(cmd: &mut Command) -> Result<()> {
66+
let output = cmd.output()?;
67+
if output.status.success() {
68+
return Ok(());
69+
}
70+
let mut err = format!("command failed {:?}", cmd);
71+
err.push_str(&format!("\nstatus: {}", output.status));
72+
err.push_str(&format!(
73+
"\nstderr:\n{}",
74+
tab(&String::from_utf8_lossy(&output.stderr))
75+
));
76+
err.push_str(&format!(
77+
"\nstdout:\n{}",
78+
tab(&String::from_utf8_lossy(&output.stdout))
79+
));
80+
bail!("{}", err);
81+
}
82+
83+
fn tab(s: &str) -> String {
84+
format!(" {}", s.replace("\n", "\n "))
85+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const assert = require('assert');
2+
const wasm = require('wasm');
3+
4+
const obj = {};
5+
assert.strictEqual(wasm.foo(obj), obj);
6+
7+
wasm.store('x');
8+
assert.strictEqual(wasm.load(), 'x');
9+
10+
const obj2 = {};
11+
wasm.store(obj2);
12+
assert.strictEqual(wasm.load(), obj2);
13+
assert.strictEqual(wasm.load(), obj2);
14+
15+
wasm.store(undefined);
16+
assert.strictEqual(wasm.load(), undefined);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
(module
2+
(func $foo (param anyref) (result anyref)
3+
local.get 0)
4+
5+
(func $store (param anyref)
6+
i32.const 0
7+
local.get 0
8+
table.set 0)
9+
10+
(func $load (result anyref)
11+
i32.const 0
12+
table.get 0)
13+
14+
(table 1 anyref)
15+
16+
(@interface func (export "foo") (param anyref) (result anyref)
17+
arg.get 0
18+
call-core $foo)
19+
20+
(@interface func (export "store") (param anyref)
21+
arg.get 0
22+
call-core $store)
23+
24+
(@interface func (export "load") (result anyref)
25+
call-core $load)
26+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const wasm = require('wasm');
2+
const assert = require('assert');
3+
4+
assert.strictEqual(wasm.foo(), 0);
5+
assert.strictEqual(wasm.get(), 1);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
(module
2+
(global $ctr (mut i32) (i32.const 0))
3+
4+
(func $increment
5+
global.get $ctr
6+
i32.const 1
7+
i32.add
8+
global.set $ctr)
9+
10+
(func $get (result i32)
11+
global.get $ctr)
12+
13+
(@interface func (export "foo") (result s32)
14+
defer-call-core $increment
15+
call-core $get
16+
i32-to-s32)
17+
18+
(@interface func (export "get") (result s32)
19+
call-core $get
20+
i32-to-s32)
21+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
const m = require('wasm');
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(module)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const assert = require('assert');
2+
const wasm = require('wasm');
3+
4+
assert.strictEqual(wasm.add_i8(0, 1), 1);
5+
assert.strictEqual(wasm.add_u8(0, 1), 1);
6+
assert.strictEqual(wasm.add_i16(0, 1), 1);
7+
assert.strictEqual(wasm.add_u16(0, 1), 1);
8+
assert.strictEqual(wasm.add_i32(0, 1), 1);
9+
assert.strictEqual(wasm.add_u32(0, 1), 1);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
(module
2+
(func $add_i32 (param i32 i32) (result i32)
3+
local.get 0
4+
local.get 1
5+
i32.add)
6+
7+
(@interface func (export "add_i8") (param s8) (param s8) (result s8)
8+
arg.get 0
9+
s8-to-i32
10+
arg.get 1
11+
s8-to-i32
12+
call-core $add_i32
13+
i32-to-s8)
14+
15+
(@interface func (export "add_i16") (param s16) (param s16) (result s16)
16+
arg.get 0
17+
s16-to-i32
18+
arg.get 1
19+
s16-to-i32
20+
call-core $add_i32
21+
i32-to-s16)
22+
23+
(@interface func (export "add_i32") (param s32) (param s32) (result s32)
24+
arg.get 0
25+
s32-to-i32
26+
arg.get 1
27+
s32-to-i32
28+
call-core $add_i32
29+
i32-to-s32)
30+
31+
(@interface func (export "add_u8") (param s8) (param s8) (result s8)
32+
arg.get 0
33+
s8-to-i32
34+
arg.get 1
35+
s8-to-i32
36+
call-core $add_i32
37+
i32-to-s8)
38+
39+
(@interface func (export "add_u16") (param u16) (param u16) (result u16)
40+
arg.get 0
41+
u16-to-i32
42+
arg.get 1
43+
u16-to-i32
44+
call-core $add_i32
45+
i32-to-u16)
46+
47+
(@interface func (export "add_u32") (param u32) (param u32) (result u32)
48+
arg.get 0
49+
u32-to-i32
50+
arg.get 1
51+
u32-to-i32
52+
call-core $add_i32
53+
i32-to-u32)
54+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const wasm = require('wasm');
2+
const assert = require('assert');
3+
4+
assert.strictEqual(wasm.foo(), 'foo');
5+
assert.strictEqual(wasm.hexa(), 'hexa');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
(module
2+
(memory 1)
3+
4+
(func $foo (result i32 i32)
5+
i32.const 0
6+
i32.const 3)
7+
(func $hexa (result i32 i32)
8+
i32.const 10
9+
i32.const 4)
10+
11+
(data (i32.const 0) "foo")
12+
(data (i32.const 10) "hexa")
13+
14+
(@interface func (export "foo") (result string)
15+
call-core $foo
16+
memory-to-string)
17+
18+
(@interface func (export "hexa") (result string)
19+
call-core $hexa
20+
memory-to-string)
21+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const assert = require('assert');
2+
const wasm = require('wasm');
3+
4+
wasm.nop();
5+
assert.strictEqual(wasm.roundtrip(1), 1);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(module
2+
(@interface func (export "nop"))
3+
(@interface func (export "roundtrip") (param s32) (result s32)
4+
arg.get 0)
5+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const wasm = require('wasm');
2+
const assert = require('assert');
3+
4+
const test = s => {
5+
wasm.set(s);
6+
assert.strictEqual(s, wasm.get());
7+
};
8+
9+
test('');
10+
test('x');
11+
test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
(module
2+
(memory 1)
3+
4+
(global $glen (mut i32) (i32.const 0))
5+
(global $gptr (mut i32) (i32.const 0))
6+
7+
(func $malloc (param i32) (result i32) i32.const 23)
8+
9+
(func $set (param $ptr i32) (param $len i32)
10+
local.get $ptr
11+
global.set $gptr
12+
local.get $len
13+
global.set $glen)
14+
15+
(func $get (result i32 i32)
16+
global.get $gptr
17+
global.get $glen)
18+
19+
(@interface func (export "set") (param string)
20+
arg.get 0
21+
string-to-memory $malloc
22+
call-core $set)
23+
24+
(@interface func (export "get") (result string)
25+
call-core $get
26+
memory-to-string)
27+
)

crates/threads-xform/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ impl Config {
105105
}
106106

107107
let memory = wasm_conventions::get_memory(module)?;
108-
let stack_pointer = wasm_conventions::get_shadow_stack_pointer(module)?;
108+
let stack_pointer = wasm_conventions::get_shadow_stack_pointer(module)
109+
.ok_or_else(|| anyhow!("failed to find shadow stack pointer"))?;
109110
let addr = allocate_static_data(module, memory, 4, 4)?;
110111
let zero = InitExpr::Value(Value::I32(0));
111112
let globals = Globals {

0 commit comments

Comments
 (0)