-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Rewrite wasm-bindgen with updated interface types proposal #1882
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
alexcrichton
merged 36 commits into
rustwasm:master
from
alexcrichton:wasm-interface-types
Dec 3, 2019
Merged
Changes from 25 commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
5595132
wip
alexcrichton 9c63620
Lots of initial work for wasm interface types
alexcrichton 061e463
Uncomment and fix `wit/mod.rs`
alexcrichton 41455de
Implement support for loading standard sections
alexcrichton f6710bf
wip
alexcrichton 8a102fe
Get a lot more pieces up and at least compiling
alexcrichton c019a03
More work in progress
alexcrichton 0f8c16c
Another round of attempts
alexcrichton d334ff8
Mliestone: producing parseable JS!
alexcrichton cab1b08
Milestone, main wasm test suite passing!
alexcrichton 0f21a8a
Now passing the anyref test suite!
alexcrichton 3298373
Depend on git wit-walrus
alexcrichton eca0e54
Merge remote-tracking branch 'origin/master' into wasm-interface-types
alexcrichton 4664199
Fix botched merge
alexcrichton 93c31b5
Remove unnecessary commented out code
alexcrichton 20ac2dc
Beef up a test about optional anyref types
alexcrichton 7f2729e
Uncomment and fix init function generation
alexcrichton 46ba8b6
Try to fix compilation of serde feature
alexcrichton 7495d4d
Add some unit tests for the anyref transform
alexcrichton 8210940
Add some more anyref transform tests
alexcrichton cbdbeb2
Add an anyref function table test
alexcrichton 24818fa
Add some tests for the multivalue transform
alexcrichton 69261ea
Run rustfmt
alexcrichton a1e2203
Run multi-value tests on CI
alexcrichton a4bce8f
Fix the typescript output of wasm-bindgen
alexcrichton 1487c98
Update to published versions of all crates
alexcrichton 07bd358
Remove magical names for anyref items
alexcrichton 0690f2e
Fix a typo
alexcrichton 20091ce
Add some comments around JS generation
alexcrichton 959e8a8
Comment out dead code for now
alexcrichton f83f377
Remove some typos
alexcrichton 18ba7ee
Remove commented out modules
alexcrichton 30af757
Improve tests for optional slices
alexcrichton a042729
Don't allow dead code in the CLI any more
alexcrichton 9867d21
Fix logic for logging errors on imports
alexcrichton 79a911c
Re-enable the direct import optimization
alexcrichton File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
//! A small test framework to execute a test function over all files in a | ||
//! directory. | ||
//! | ||
//! Each file in the directory has its own `CHECK-ALL` annotation indicating the | ||
//! expected output of the test. That can be automatically updated with | ||
//! `BLESS=1` in the environment. Otherwise the test are checked against the | ||
//! listed expectation. | ||
|
||
use anyhow::{anyhow, bail, Context, Result}; | ||
use rayon::prelude::*; | ||
use std::env; | ||
use std::fs; | ||
use std::path::{Path, PathBuf}; | ||
use wast::parser::{Parse, Parser}; | ||
|
||
fn main() { | ||
run("tests".as_ref(), runtest); | ||
} | ||
|
||
fn runtest(test: &Test) -> Result<String> { | ||
let wasm = wat::parse_file(&test.file)?; | ||
let mut walrus = walrus::Module::from_buffer(&wasm)?; | ||
let mut cx = wasm_bindgen_anyref_xform::Context::default(); | ||
cx.prepare(&mut walrus)?; | ||
for directive in test.directives.iter() { | ||
match &directive.kind { | ||
DirectiveKind::Export(name) => { | ||
let export = walrus | ||
.exports | ||
.iter() | ||
.find(|e| e.name == *name) | ||
.ok_or_else(|| anyhow!("failed to find export"))?; | ||
cx.export_xform(export.id(), &directive.args, directive.ret_anyref); | ||
} | ||
DirectiveKind::Import(module, field) => { | ||
let import = walrus | ||
.imports | ||
.iter() | ||
.find(|e| e.module == *module && e.name == *field) | ||
.ok_or_else(|| anyhow!("failed to find export"))?; | ||
cx.import_xform(import.id(), &directive.args, directive.ret_anyref); | ||
} | ||
DirectiveKind::Table(idx) => { | ||
cx.table_element_xform(*idx, &directive.args, directive.ret_anyref); | ||
} | ||
} | ||
} | ||
cx.run(&mut walrus)?; | ||
walrus::passes::gc::run(&mut walrus); | ||
let printed = wasmprinter::print_bytes(&walrus.emit_wasm())?; | ||
Ok(printed) | ||
} | ||
|
||
fn run(dir: &Path, run: fn(&Test) -> Result<String>) { | ||
let mut tests = Vec::new(); | ||
find_tests(dir, &mut tests); | ||
let filter = std::env::args().nth(1); | ||
|
||
let bless = env::var("BLESS").is_ok(); | ||
let tests = tests | ||
.iter() | ||
.filter(|test| { | ||
if let Some(filter) = &filter { | ||
if let Some(s) = test.file_name().and_then(|s| s.to_str()) { | ||
if !s.contains(filter) { | ||
return false; | ||
} | ||
} | ||
} | ||
true | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
println!("\nrunning {} tests\n", tests.len()); | ||
|
||
let errors = tests | ||
.par_iter() | ||
.filter_map(|test| run_test(test, bless, run).err()) | ||
.collect::<Vec<_>>(); | ||
|
||
if !errors.is_empty() { | ||
for msg in errors.iter() { | ||
eprintln!("error: {:?}", msg); | ||
} | ||
|
||
panic!("{} tests failed", errors.len()) | ||
} | ||
|
||
println!("test result: ok. {} passed\n", tests.len()); | ||
} | ||
|
||
fn run_test(test: &Path, bless: bool, run: fn(&Test) -> anyhow::Result<String>) -> Result<()> { | ||
(|| -> Result<_> { | ||
let expected = Test::from_file(test)?; | ||
let actual = run(&expected)?; | ||
expected.check(&actual, bless)?; | ||
Ok(()) | ||
})() | ||
.context(format!("test failed - {}", test.display()))?; | ||
Ok(()) | ||
} | ||
|
||
fn find_tests(path: &Path, tests: &mut Vec<PathBuf>) { | ||
for f in path.read_dir().unwrap() { | ||
let f = f.unwrap(); | ||
if f.file_type().unwrap().is_dir() { | ||
find_tests(&f.path(), tests); | ||
continue; | ||
} | ||
match f.path().extension().and_then(|s| s.to_str()) { | ||
Some("wat") => {} | ||
_ => continue, | ||
} | ||
tests.push(f.path()); | ||
} | ||
} | ||
|
||
struct Test { | ||
file: PathBuf, | ||
directives: Vec<Directive>, | ||
assertion: Option<String>, | ||
} | ||
|
||
struct Directive { | ||
args: Vec<(usize, bool)>, | ||
ret_anyref: bool, | ||
kind: DirectiveKind, | ||
} | ||
|
||
enum DirectiveKind { | ||
Import(String, String), | ||
Export(String), | ||
Table(u32), | ||
} | ||
|
||
impl Test { | ||
fn from_file(path: &Path) -> Result<Test> { | ||
let contents = fs::read_to_string(path)?; | ||
let mut iter = contents.lines(); | ||
let mut assertion = None; | ||
let mut directives = Vec::new(); | ||
while let Some(line) = iter.next() { | ||
if line.starts_with("(; CHECK-ALL:") { | ||
let mut pattern = String::new(); | ||
while let Some(line) = iter.next() { | ||
if line == ";)" { | ||
break; | ||
} | ||
pattern.push_str(line); | ||
pattern.push_str("\n"); | ||
} | ||
while pattern.ends_with("\n") { | ||
pattern.pop(); | ||
} | ||
if iter.next().is_some() { | ||
bail!("CHECK-ALL must be at the end of the file"); | ||
} | ||
assertion = Some(pattern); | ||
continue; | ||
} | ||
|
||
if !line.starts_with(";; @xform") { | ||
continue; | ||
} | ||
let directive = &line[9..]; | ||
let buf = wast::parser::ParseBuffer::new(directive)?; | ||
directives.push(wast::parser::parse::<Directive>(&buf)?); | ||
} | ||
Ok(Test { | ||
file: path.to_path_buf(), | ||
directives, | ||
assertion, | ||
}) | ||
} | ||
|
||
fn check(&self, output: &str, bless: bool) -> Result<()> { | ||
if bless { | ||
update_output(&self.file, output) | ||
} else if let Some(pattern) = &self.assertion { | ||
if output == pattern { | ||
return Ok(()); | ||
} | ||
bail!( | ||
"expected\n {}\n\nactual\n {}", | ||
pattern.replace("\n", "\n "), | ||
output.replace("\n", "\n ") | ||
); | ||
} else { | ||
bail!( | ||
"no test assertions were found in this file, but you can \ | ||
rerun tests with `BLESS=1` to automatically add assertions \ | ||
to this file" | ||
); | ||
} | ||
} | ||
} | ||
|
||
fn update_output(path: &Path, output: &str) -> Result<()> { | ||
let contents = fs::read_to_string(path)?; | ||
let start = contents.find("(; CHECK-ALL:").unwrap_or(contents.len()); | ||
|
||
let mut new_output = String::new(); | ||
for line in output.lines() { | ||
new_output.push_str(line); | ||
new_output.push_str("\n"); | ||
} | ||
let new = format!( | ||
"{}\n\n(; CHECK-ALL:\n{}\n;)\n", | ||
contents[..start].trim(), | ||
new_output.trim_end() | ||
); | ||
fs::write(path, new)?; | ||
Ok(()) | ||
} | ||
|
||
impl<'a> Parse<'a> for Directive { | ||
fn parse(parser: Parser<'a>) -> wast::parser::Result<Self> { | ||
use wast::kw; | ||
wast::custom_keyword!(anyref_owned); | ||
wast::custom_keyword!(anyref_borrowed); | ||
wast::custom_keyword!(other); | ||
|
||
let kind = if parser.peek::<kw::import>() { | ||
parser.parse::<kw::import>()?; | ||
DirectiveKind::Import(parser.parse()?, parser.parse()?) | ||
} else if parser.peek::<kw::export>() { | ||
parser.parse::<kw::export>()?; | ||
DirectiveKind::Export(parser.parse()?) | ||
} else { | ||
parser.parse::<kw::table>()?; | ||
DirectiveKind::Table(parser.parse()?) | ||
}; | ||
let mut args = Vec::new(); | ||
parser.parens(|p| { | ||
let mut i = 0; | ||
while !p.is_empty() { | ||
if parser.peek::<anyref_owned>() { | ||
parser.parse::<anyref_owned>()?; | ||
args.push((i, true)); | ||
} else if parser.peek::<anyref_borrowed>() { | ||
parser.parse::<anyref_borrowed>()?; | ||
args.push((i, false)); | ||
} else { | ||
parser.parse::<other>()?; | ||
} | ||
i += 1; | ||
} | ||
Ok(()) | ||
})?; | ||
|
||
let ret_anyref = parser.parse::<Option<anyref_owned>>()?.is_some(); | ||
Ok(Directive { | ||
args, | ||
ret_anyref, | ||
kind, | ||
}) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
;; @xform export "foo" (anyref_owned) | ||
|
||
(module | ||
(func $foo (export "foo") (param i32)) | ||
(func $alloc (export "__wbindgen_anyref_table_alloc") (result i32) | ||
i32.const 0) | ||
(func $dealloc (export "__wbindgen_anyref_table_dealloc") (param i32)) | ||
) | ||
|
||
(; CHECK-ALL: | ||
(module | ||
(type (;0;) (func (result i32))) | ||
(type (;1;) (func (param i32))) | ||
(type (;2;) (func (param anyref))) | ||
(func $foo anyref shim (type 2) (param anyref) | ||
(local i32) | ||
call $alloc | ||
local.tee 1 | ||
local.get 0 | ||
table.set 0 | ||
local.get 1 | ||
call $foo) | ||
(func $alloc (type 0) (result i32) | ||
i32.const 0) | ||
(func $foo (type 1) (param i32)) | ||
(func $dealloc (type 1) (param i32)) | ||
(table (;0;) 32 anyref) | ||
(export "foo" (func $foo anyref shim)) | ||
(export "__wbindgen_anyref_table_alloc" (func $alloc)) | ||
(export "__wbindgen_anyref_table_dealloc" (func $dealloc))) | ||
;) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
;; @xform export "foo" (anyref_borrowed) | ||
|
||
(module | ||
(func $foo (export "foo") (param i32)) | ||
(func $alloc (export "__wbindgen_anyref_table_alloc") (result i32) | ||
i32.const 0) | ||
(func $dealloc (export "__wbindgen_anyref_table_dealloc") (param i32)) | ||
) | ||
|
||
(; CHECK-ALL: | ||
(module | ||
(type (;0;) (func (result i32))) | ||
(type (;1;) (func (param i32))) | ||
(type (;2;) (func (param anyref))) | ||
(func $foo anyref shim (type 2) (param anyref) | ||
(local i32) | ||
global.get 0 | ||
i32.const 1 | ||
i32.sub | ||
local.tee 1 | ||
global.set 0 | ||
local.get 1 | ||
local.get 0 | ||
table.set 0 | ||
local.get 1 | ||
call $foo | ||
local.get 1 | ||
ref.null | ||
table.set 0 | ||
local.get 1 | ||
i32.const 1 | ||
i32.add | ||
global.set 0) | ||
(func $alloc (type 0) (result i32) | ||
i32.const 0) | ||
(func $foo (type 1) (param i32)) | ||
(func $dealloc (type 1) (param i32)) | ||
(table (;0;) 32 anyref) | ||
(global (;0;) (mut i32) (i32.const 32)) | ||
(export "foo" (func $foo anyref shim)) | ||
(export "__wbindgen_anyref_table_alloc" (func $alloc)) | ||
(export "__wbindgen_anyref_table_dealloc" (func $dealloc))) | ||
;) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. Maybe worth pulling our simple filecheck implementation into a reusable crate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been tweaking this as I've gone along for various crates, but it's starting to reach a steady state, yeah, so we should probably look into extracting it soon!