diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index b4acef55..95fbd865 100644 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -10,10 +10,12 @@ extern crate wasmi; #[macro_use] extern crate failure; +use std::any::Any; use std::collections::BTreeSet; use std::fmt; use std::fs::File; use std::io::{Read, Write}; +use std::mem; use std::path::{Path, PathBuf}; use failure::{Error, ResultExt}; @@ -24,7 +26,7 @@ mod js; pub mod wasm2es6js; pub struct Bindgen { - path: Option, + input: Input, nodejs: bool, nodejs_experimental_modules: bool, browser: bool, @@ -36,10 +38,17 @@ pub struct Bindgen { keep_debug: bool, } +enum Input { + Path(PathBuf), + Bytes(Vec, String), + Module(Module, String), + None, +} + impl Bindgen { pub fn new() -> Bindgen { Bindgen { - path: None, + input: Input::None, nodejs: false, nodejs_experimental_modules: false, browser: false, @@ -53,7 +62,37 @@ impl Bindgen { } pub fn input_path>(&mut self, path: P) -> &mut Bindgen { - self.path = Some(path.as_ref().to_path_buf()); + self.input = Input::Path(path.as_ref().to_path_buf()); + self + } + + /// Explicitly specify the already parsed input module. + /// + /// Note that this API is a little wonky to avoid tying itself with a public + /// dependency on the `parity-wasm` crate, what we currently use to parse + /// wasm mdoules. + /// + /// If the `module` argument is a `parity_wasm::Module` then it will be used + /// directly. Otherwise it will be passed to `into_bytes` to serialize the + /// module to a vector of bytes, and this will deserialize the module later. + /// + /// Note that even if the argument passed in is a `parity_wasm::Module` it + /// doesn't mean that this won't invoke `into_bytes`, if the `parity_wasm` + /// crate versions are different we'll have to go through serialization. + pub fn input_module( + &mut self, + name: &str, + mut module: T, + into_bytes: impl FnOnce(T) -> Vec, + ) -> &mut Bindgen { + let name = name.to_string(); + if let Some(module) = (&mut module as &mut Any).downcast_mut::() { + let blank = Module::new(Vec::new()); + self.input = Input::Module(mem::replace(module, blank), name); + return self + } + + self.input = Input::Bytes(into_bytes(module), name); self } @@ -107,17 +146,28 @@ impl Bindgen { } fn _generate(&mut self, out_dir: &Path) -> Result<(), Error> { - let input = match self.path { - Some(ref path) => path, - None => bail!("must have a path input for now"), + let (mut module, stem) = match self.input { + Input::None => bail!("must have an input by now"), + Input::Module(ref mut m, ref name) => { + let blank_module = Module::new(Vec::new()); + (mem::replace(m, blank_module), &name[..]) + } + Input::Bytes(ref b, ref name) => { + let module = parity_wasm::deserialize_buffer::(&b) + .context("failed to parse input file as wasm")?; + (module, &name[..]) + } + Input::Path(ref path) => { + let mut contents = Vec::new(); + File::open(&path) + .and_then(|mut f| f.read_to_end(&mut contents)) + .with_context(|_| format!("failed to read `{}`", path.display()))?; + let module = parity_wasm::deserialize_buffer::(&contents) + .context("failed to parse input file as wasm")?; + let stem = path.file_stem().unwrap().to_str().unwrap(); + (module, stem) + } }; - let stem = input.file_stem().unwrap().to_str().unwrap(); - let mut contents = Vec::new(); - File::open(&input) - .and_then(|mut f| f.read_to_end(&mut contents)) - .with_context(|_| format!("failed to read `{}`", input.display()))?; - let mut module = parity_wasm::deserialize_buffer::(&contents) - .with_context(|_| "failed to parse input file as wasm")?; let programs = extract_programs(&mut module) .with_context(|_| "failed to extract wasm-bindgen custom sections")?; diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner.rs b/crates/cli/src/bin/wasm-bindgen-test-runner.rs index 32f9a7c7..36be64c4 100644 --- a/crates/cli/src/bin/wasm-bindgen-test-runner.rs +++ b/crates/cli/src/bin/wasm-bindgen-test-runner.rs @@ -50,16 +50,6 @@ fn rmain() -> Result<(), Error> { fs::create_dir(&tmpdir) .context("creating temporary directory")?; - // For now unconditionally generate wasm-bindgen code tailored for node.js, - // but eventually we'll want more options here for browsers! - let mut b = Bindgen::new(); - b.debug(true) - .nodejs(true) - .input_path(&wasm_file_to_test) - .keep_debug(false) - .generate(&tmpdir) - .context("executing `wasm-bindgen` over the wasm file")?; - let module = wasm_file_to_test.file_stem() .and_then(|s| s.to_str()) .ok_or_else(|| format_err!("invalid filename passed in"))?; @@ -118,12 +108,12 @@ fn rmain() -> Result<(), Error> { // execute, and then those objects are passed into wasm for it to execute // when it sees fit. let mut wasm = Vec::new(); - let wasm_file = tmpdir.join(format!("{}_bg.wasm", module)); - File::open(wasm_file).and_then(|mut f| f.read_to_end(&mut wasm)) + File::open(&wasm_file_to_test) + .and_then(|mut f| f.read_to_end(&mut wasm)) .context("failed to read wasm file")?; - let module = Module::deserialize(&mut &wasm[..]) + let wasm = Module::deserialize(&mut &wasm[..]) .context("failed to deserialize wasm module")?; - if let Some(exports) = module.export_section() { + if let Some(exports) = wasm.export_section() { for export in exports.entries() { if !export.field().starts_with("__wbg_test") { continue @@ -135,6 +125,16 @@ fn rmain() -> Result<(), Error> { // And as a final addendum, exit with a nonzero code if any tests fail. js_to_execute.push_str("if (!cx.run(tests)) exit(1);\n"); + // For now unconditionally generate wasm-bindgen code tailored for node.js, + // but eventually we'll want more options here for browsers! + let mut b = Bindgen::new(); + b.debug(true) + .nodejs(true) + .input_module(module, wasm, |m| parity_wasm::serialize(m).unwrap()) + .keep_debug(false) + .generate(&tmpdir) + .context("executing `wasm-bindgen` over the wasm file")?; + let js_path = tmpdir.join("run.js"); File::create(&js_path) .and_then(|mut f| f.write_all(js_to_execute.as_bytes()))