mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-05-02 17:12:15 +00:00
Merge pull request #556 from alexcrichton/fewer-copies
Optimize the execution time of wasm-bindgen tools
This commit is contained in:
commit
54109f9ccf
@ -19,5 +19,5 @@ serde_derive = "1.0"
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tempfile = "3.0"
|
tempfile = "3.0"
|
||||||
wasm-bindgen-shared = { path = "../shared", version = '=0.2.14' }
|
wasm-bindgen-shared = { path = "../shared", version = '=0.2.14' }
|
||||||
wasm-gc-api = "0.1.8"
|
wasm-gc-api = "0.1.9"
|
||||||
wasmi = "0.3"
|
wasmi = "0.3"
|
||||||
|
@ -1580,12 +1580,14 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
fn gc(&mut self) -> Result<(), Error> {
|
fn gc(&mut self) -> Result<(), Error> {
|
||||||
let module = mem::replace(self.module, Module::default());
|
let module = mem::replace(self.module, Module::default());
|
||||||
let wasm_bytes = parity_wasm::serialize(module)?;
|
let result = wasm_gc::Config::new()
|
||||||
let bytes = wasm_gc::Config::new()
|
|
||||||
.demangle(self.config.demangle)
|
.demangle(self.config.demangle)
|
||||||
.keep_debug(self.config.keep_debug || self.config.debug)
|
.keep_debug(self.config.keep_debug || self.config.debug)
|
||||||
.gc(&wasm_bytes)?;
|
.run(module, |m| parity_wasm::serialize(m).unwrap())?;
|
||||||
*self.module = deserialize_buffer(&bytes)?;
|
*self.module = match result.into_module() {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(result) => deserialize_buffer(&result.into_bytes()?)?,
|
||||||
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,10 +10,11 @@ extern crate wasmi;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fs::File;
|
use std::fs;
|
||||||
use std::io::{Read, Write};
|
use std::mem;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use failure::{Error, ResultExt};
|
use failure::{Error, ResultExt};
|
||||||
@ -24,7 +25,7 @@ mod js;
|
|||||||
pub mod wasm2es6js;
|
pub mod wasm2es6js;
|
||||||
|
|
||||||
pub struct Bindgen {
|
pub struct Bindgen {
|
||||||
path: Option<PathBuf>,
|
input: Input,
|
||||||
nodejs: bool,
|
nodejs: bool,
|
||||||
nodejs_experimental_modules: bool,
|
nodejs_experimental_modules: bool,
|
||||||
browser: bool,
|
browser: bool,
|
||||||
@ -36,10 +37,17 @@ pub struct Bindgen {
|
|||||||
keep_debug: bool,
|
keep_debug: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Input {
|
||||||
|
Path(PathBuf),
|
||||||
|
Bytes(Vec<u8>, String),
|
||||||
|
Module(Module, String),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
impl Bindgen {
|
impl Bindgen {
|
||||||
pub fn new() -> Bindgen {
|
pub fn new() -> Bindgen {
|
||||||
Bindgen {
|
Bindgen {
|
||||||
path: None,
|
input: Input::None,
|
||||||
nodejs: false,
|
nodejs: false,
|
||||||
nodejs_experimental_modules: false,
|
nodejs_experimental_modules: false,
|
||||||
browser: false,
|
browser: false,
|
||||||
@ -53,7 +61,37 @@ impl Bindgen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_path<P: AsRef<Path>>(&mut self, path: P) -> &mut Bindgen {
|
pub fn input_path<P: AsRef<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<T: Any>(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
mut module: T,
|
||||||
|
into_bytes: impl FnOnce(T) -> Vec<u8>,
|
||||||
|
) -> &mut Bindgen {
|
||||||
|
let name = name.to_string();
|
||||||
|
if let Some(module) = (&mut module as &mut Any).downcast_mut::<Module>() {
|
||||||
|
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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,17 +145,26 @@ impl Bindgen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn _generate(&mut self, out_dir: &Path) -> Result<(), Error> {
|
fn _generate(&mut self, out_dir: &Path) -> Result<(), Error> {
|
||||||
let input = match self.path {
|
let (mut module, stem) = match self.input {
|
||||||
Some(ref path) => path,
|
Input::None => bail!("must have an input by now"),
|
||||||
None => bail!("must have a path input for 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::<Module>(&b)
|
||||||
|
.context("failed to parse input file as wasm")?;
|
||||||
|
(module, &name[..])
|
||||||
|
}
|
||||||
|
Input::Path(ref path) => {
|
||||||
|
let contents = fs::read(&path)
|
||||||
|
.with_context(|_| format!("failed to read `{}`", path.display()))?;
|
||||||
|
let module = parity_wasm::deserialize_buffer::<Module>(&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::<Module>(&contents)
|
|
||||||
.with_context(|_| "failed to parse input file as wasm")?;
|
|
||||||
let programs = extract_programs(&mut module)
|
let programs = extract_programs(&mut module)
|
||||||
.with_context(|_| "failed to extract wasm-bindgen custom sections")?;
|
.with_context(|_| "failed to extract wasm-bindgen custom sections")?;
|
||||||
|
|
||||||
@ -134,7 +181,9 @@ impl Bindgen {
|
|||||||
// This means that whenever we encounter an import or export we'll
|
// This means that whenever we encounter an import or export we'll
|
||||||
// execute a shim function which informs us about its type so we can
|
// execute a shim function which informs us about its type so we can
|
||||||
// then generate the appropriate bindings.
|
// then generate the appropriate bindings.
|
||||||
let instance = wasmi::Module::from_buffer(&contents)
|
//
|
||||||
|
// TODO: avoid a `clone` here of the module if we can
|
||||||
|
let instance = wasmi::Module::from_parity_wasm_module(module.clone())
|
||||||
.with_context(|_| "failed to create wasmi module")?;
|
.with_context(|_| "failed to create wasmi module")?;
|
||||||
let instance = wasmi::ModuleInstance::new(&instance, &MyResolver)
|
let instance = wasmi::ModuleInstance::new(&instance, &MyResolver)
|
||||||
.with_context(|_| "failed to instantiate wasm module")?;
|
.with_context(|_| "failed to instantiate wasm module")?;
|
||||||
@ -180,14 +229,12 @@ impl Bindgen {
|
|||||||
|
|
||||||
let extension = if self.nodejs_experimental_modules { "mjs" } else { "js" };
|
let extension = if self.nodejs_experimental_modules { "mjs" } else { "js" };
|
||||||
let js_path = out_dir.join(stem).with_extension(extension);
|
let js_path = out_dir.join(stem).with_extension(extension);
|
||||||
File::create(&js_path)
|
fs::write(&js_path, reset_indentation(&js))
|
||||||
.and_then(|mut f| f.write_all(reset_indentation(&js).as_bytes()))
|
|
||||||
.with_context(|_| format!("failed to write `{}`", js_path.display()))?;
|
.with_context(|_| format!("failed to write `{}`", js_path.display()))?;
|
||||||
|
|
||||||
if self.typescript {
|
if self.typescript {
|
||||||
let ts_path = out_dir.join(stem).with_extension("d.ts");
|
let ts_path = out_dir.join(stem).with_extension("d.ts");
|
||||||
File::create(&ts_path)
|
fs::write(&ts_path, ts)
|
||||||
.and_then(|mut f| f.write_all(ts.as_bytes()))
|
|
||||||
.with_context(|_| format!("failed to write `{}`", ts_path.display()))?;
|
.with_context(|_| format!("failed to write `{}`", ts_path.display()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,14 +243,12 @@ impl Bindgen {
|
|||||||
if self.nodejs {
|
if self.nodejs {
|
||||||
let js_path = wasm_path.with_extension(extension);
|
let js_path = wasm_path.with_extension(extension);
|
||||||
let shim = self.generate_node_wasm_import(&module, &wasm_path);
|
let shim = self.generate_node_wasm_import(&module, &wasm_path);
|
||||||
File::create(&js_path)
|
fs::write(&js_path, shim)
|
||||||
.and_then(|mut f| f.write_all(shim.as_bytes()))
|
|
||||||
.with_context(|_| format!("failed to write `{}`", js_path.display()))?;
|
.with_context(|_| format!("failed to write `{}`", js_path.display()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let wasm_bytes = parity_wasm::serialize(module)?;
|
let wasm_bytes = parity_wasm::serialize(module)?;
|
||||||
File::create(&wasm_path)
|
fs::write(&wasm_path, wasm_bytes)
|
||||||
.and_then(|mut f| f.write_all(&wasm_bytes))
|
|
||||||
.with_context(|_| format!("failed to write `{}`", wasm_path.display()))?;
|
.with_context(|_| format!("failed to write `{}`", wasm_path.display()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ extern crate base64;
|
|||||||
extern crate tempfile;
|
extern crate tempfile;
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fs::File;
|
use std::fs;
|
||||||
use std::io::{self, Read, Write};
|
use std::io;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use failure::{Error, ResultExt};
|
use failure::{Error, ResultExt};
|
||||||
@ -375,8 +375,7 @@ impl Output {
|
|||||||
let td = tempfile::tempdir()?;
|
let td = tempfile::tempdir()?;
|
||||||
let wasm = serialize(self.module)?;
|
let wasm = serialize(self.module)?;
|
||||||
let wasm_file = td.as_ref().join("foo.wasm");
|
let wasm_file = td.as_ref().join("foo.wasm");
|
||||||
File::create(&wasm_file)
|
fs::write(&wasm_file, wasm)
|
||||||
.and_then(|mut f| f.write_all(&wasm))
|
|
||||||
.with_context(|_| format!("failed to write wasm to `{}`", wasm_file.display()))?;
|
.with_context(|_| format!("failed to write wasm to `{}`", wasm_file.display()))?;
|
||||||
|
|
||||||
let wast_file = td.as_ref().join("foo.wast");
|
let wast_file = td.as_ref().join("foo.wast");
|
||||||
@ -396,9 +395,7 @@ impl Output {
|
|||||||
"wasm2asm",
|
"wasm2asm",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut asm_func = String::new();
|
let asm_func = fs::read_to_string(&js_file)
|
||||||
File::open(&js_file)
|
|
||||||
.and_then(|mut f| f.read_to_string(&mut asm_func))
|
|
||||||
.with_context(|_| format!("failed to read `{}`", js_file.display()))?;
|
.with_context(|_| format!("failed to read `{}`", js_file.display()))?;
|
||||||
|
|
||||||
let mut make_imports = String::from(
|
let mut make_imports = String::from(
|
||||||
|
@ -4,8 +4,7 @@ extern crate wasm_bindgen_cli_support;
|
|||||||
extern crate parity_wasm;
|
extern crate parity_wasm;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::{self, File};
|
use std::fs;
|
||||||
use std::io::{Write, Read};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::{self, Command};
|
use std::process::{self, Command};
|
||||||
|
|
||||||
@ -50,16 +49,6 @@ fn rmain() -> Result<(), Error> {
|
|||||||
fs::create_dir(&tmpdir)
|
fs::create_dir(&tmpdir)
|
||||||
.context("creating temporary directory")?;
|
.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()
|
let module = wasm_file_to_test.file_stem()
|
||||||
.and_then(|s| s.to_str())
|
.and_then(|s| s.to_str())
|
||||||
.ok_or_else(|| format_err!("invalid filename passed in"))?;
|
.ok_or_else(|| format_err!("invalid filename passed in"))?;
|
||||||
@ -117,13 +106,11 @@ fn rmain() -> Result<(), Error> {
|
|||||||
// Note that we're collecting *JS objects* that represent the functions to
|
// Note that we're collecting *JS objects* that represent the functions to
|
||||||
// execute, and then those objects are passed into wasm for it to execute
|
// execute, and then those objects are passed into wasm for it to execute
|
||||||
// when it sees fit.
|
// when it sees fit.
|
||||||
let mut wasm = Vec::new();
|
let wasm = fs::read(&wasm_file_to_test)
|
||||||
let wasm_file = tmpdir.join(format!("{}_bg.wasm", module));
|
|
||||||
File::open(wasm_file).and_then(|mut f| f.read_to_end(&mut wasm))
|
|
||||||
.context("failed to read wasm file")?;
|
.context("failed to read wasm file")?;
|
||||||
let module = Module::deserialize(&mut &wasm[..])
|
let wasm = Module::deserialize(&mut &wasm[..])
|
||||||
.context("failed to deserialize wasm module")?;
|
.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() {
|
for export in exports.entries() {
|
||||||
if !export.field().starts_with("__wbg_test") {
|
if !export.field().starts_with("__wbg_test") {
|
||||||
continue
|
continue
|
||||||
@ -135,9 +122,18 @@ fn rmain() -> Result<(), Error> {
|
|||||||
// And as a final addendum, exit with a nonzero code if any tests fail.
|
// 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");
|
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");
|
let js_path = tmpdir.join("run.js");
|
||||||
File::create(&js_path)
|
fs::write(&js_path, js_to_execute)
|
||||||
.and_then(|mut f| f.write_all(js_to_execute.as_bytes()))
|
|
||||||
.context("failed to write JS file")?;
|
.context("failed to write JS file")?;
|
||||||
|
|
||||||
// Last but not least, execute `node`! Add an entry to `NODE_PATH` for the
|
// Last but not least, execute `node`! Add an entry to `NODE_PATH` for the
|
||||||
|
@ -4,8 +4,7 @@ extern crate docopt;
|
|||||||
extern crate failure;
|
extern crate failure;
|
||||||
extern crate wasm_bindgen_cli_support;
|
extern crate wasm_bindgen_cli_support;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs;
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
@ -58,9 +57,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rmain(args: &Args) -> Result<(), Error> {
|
fn rmain(args: &Args) -> Result<(), Error> {
|
||||||
let mut wasm = Vec::new();
|
let wasm = fs::read(&args.arg_input)
|
||||||
File::open(&args.arg_input)
|
|
||||||
.and_then(|mut f| f.read_to_end(&mut wasm))
|
|
||||||
.with_context(|_| format!("failed to read `{}`", args.arg_input.display()))?;
|
.with_context(|_| format!("failed to read `{}`", args.arg_input.display()))?;
|
||||||
|
|
||||||
let object = wasm_bindgen_cli_support::wasm2es6js::Config::new()
|
let object = wasm_bindgen_cli_support::wasm2es6js::Config::new()
|
||||||
@ -73,8 +70,7 @@ fn rmain(args: &Args) -> Result<(), Error> {
|
|||||||
if let Some(ref p) = args.flag_output {
|
if let Some(ref p) = args.flag_output {
|
||||||
let dst = p.with_extension("d.ts");
|
let dst = p.with_extension("d.ts");
|
||||||
let ts = object.typescript();
|
let ts = object.typescript();
|
||||||
File::create(&dst)
|
fs::write(&dst, ts)
|
||||||
.and_then(|mut f| f.write_all(ts.as_bytes()))
|
|
||||||
.with_context(|_| format!("failed to write `{}`", dst.display()))?;
|
.with_context(|_| format!("failed to write `{}`", dst.display()))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,8 +79,7 @@ fn rmain(args: &Args) -> Result<(), Error> {
|
|||||||
|
|
||||||
match args.flag_output {
|
match args.flag_output {
|
||||||
Some(ref p) => {
|
Some(ref p) => {
|
||||||
File::create(p)
|
fs::write(p, js)
|
||||||
.and_then(|mut f| f.write_all(js.as_bytes()))
|
|
||||||
.with_context(|_| format!("failed to write `{}`", p.display()))?;
|
.with_context(|_| format!("failed to write `{}`", p.display()))?;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user