530 lines
18 KiB
Rust
Raw Normal View History

#![doc(html_root_url = "https://docs.rs/wasm-bindgen-cli-support/0.2")]
2017-12-14 19:31:01 -08:00
extern crate parity_wasm;
extern crate wasm_bindgen_shared as shared;
#[macro_use]
extern crate serde_derive;
2017-12-14 19:31:01 -08:00
extern crate serde_json;
extern crate wasm_gc;
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
extern crate wasmi;
#[macro_use]
extern crate failure;
2017-12-14 19:31:01 -08:00
use std::any::Any;
use std::collections::BTreeSet;
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
use std::fmt;
use std::fs;
use std::mem;
use std::path::{Path, PathBuf};
2017-12-14 19:31:01 -08:00
use failure::{Error, ResultExt};
2017-12-14 19:31:01 -08:00
use parity_wasm::elements::*;
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
mod descriptor;
2018-06-27 22:42:34 -07:00
mod js;
Rewrite wasm-bindgen with ES6 modules in mind This commit is a mostly-rewrite of the `wasm-bindgen` tool. After some recent discussions it's clear that the previous model wasn't quite going to cut it, and this iteration is one which primarily embraces ES6 modules and the idea that this is a polyfill for host bindings. The overall interface and functionality hasn't changed much but the underlying technology has now changed significantly. Previously `wasm-bindgen` would emit a JS file that acted as an ES6 module but had a bit of a wonky interface. It exposed an async function for instantiation of the wasm module, but that's the bundler's job, not ours! Instead this iteration views each input and output as a discrete ES6 module. The input wasm file is interpreted as "this *should* be an ES6 module with rich types" and the output is "well here's some ES6 modules that fulfill that contract". Notably the tool now replaces the original wasm ES6 module with a JS ES6 module that has the "rich interface". Additionally a second ES6 module is emitted (the actual wasm file) which imports and exports to the original ES6 module. This strategy is hoped to be much more amenable to bundlers and controlling how the wasm itself is instantiated. The emitted files files purely assume ES6 modules and should be able to work as-is once ES6 module integration for wasm is completed. Note that there aren't a ton of tools to pretend a wasm module is an ES6 module at the moment but those should be coming soon! In the meantime a local `wasm2es6js` hack was added to help make *something* work today. The README has also been updated with instructions for interacting with this model.
2018-01-29 21:20:38 -08:00
pub mod wasm2es6js;
2017-12-18 12:39:14 -08:00
2017-12-14 19:31:01 -08:00
pub struct Bindgen {
input: Input,
2017-12-14 21:55:21 -08:00
nodejs: bool,
Speed up Travis by running Webpack in fewer tests (#381) * Reorganize Travis configuration * Add a `JOB` env var descriptor to all matrix entries. Not used anywhere but is useful when viewing the whole build on Travis's web interface. * Reorganize where builds are located, moving slow builds first and fast ones last. * Change checking the CLI builds from `cargo build` to `cargo check` * Use YAML references to reduce some duplication * Print some more timing statistics for each test * Extract `Project` helper in tests to a module This'll help make it a bit more extensible over time. At the same time the methods are also slightly reorganized to read more clearly from top to bottom. * Migrate all tests away from Webpack Wepback can take a significant amount of time to execute and when it's multiplied by hundreds of tests that adds up really quickly! After investigating Node's `--experimental-modules` option it looks like it's suitable for our use so this switches all tests to using JS files (moving away from TypeScript as well) with `--experimental-modules` with Node. Tests will be selectively re-enabled with webpack and node.js specific output (that doesn't require `--experimental-modules`), coming in later commits. * Restore the node test for node.js output Ensures it's workable as-is * Only generate typescript with webpack * Only read wasm files for webpack * Skip package.json/node_modules for now * Only generate webpack config if needed * Start a dedicated test module for typescript Will hopefully verify the generated Typescript compiles OK. * Remove unneeded `node` method * Fixup some rebase conflicts * Don't run asmjs example on travis * Fixup generator tests * Attempt to fix windows * Comment windows fix * More test fixes * More exclusions * More test fixes * Relax eslint regex Catch mjs modules as well * Fix eslint * Speed up travis on examples slightly
2018-07-04 22:37:09 -05:00
nodejs_experimental_modules: bool,
browser: bool,
2018-04-11 13:59:58 +05:45
no_modules: bool,
no_modules_global: Option<String>,
debug: bool,
Rewrite wasm-bindgen with ES6 modules in mind This commit is a mostly-rewrite of the `wasm-bindgen` tool. After some recent discussions it's clear that the previous model wasn't quite going to cut it, and this iteration is one which primarily embraces ES6 modules and the idea that this is a polyfill for host bindings. The overall interface and functionality hasn't changed much but the underlying technology has now changed significantly. Previously `wasm-bindgen` would emit a JS file that acted as an ES6 module but had a bit of a wonky interface. It exposed an async function for instantiation of the wasm module, but that's the bundler's job, not ours! Instead this iteration views each input and output as a discrete ES6 module. The input wasm file is interpreted as "this *should* be an ES6 module with rich types" and the output is "well here's some ES6 modules that fulfill that contract". Notably the tool now replaces the original wasm ES6 module with a JS ES6 module that has the "rich interface". Additionally a second ES6 module is emitted (the actual wasm file) which imports and exports to the original ES6 module. This strategy is hoped to be much more amenable to bundlers and controlling how the wasm itself is instantiated. The emitted files files purely assume ES6 modules and should be able to work as-is once ES6 module integration for wasm is completed. Note that there aren't a ton of tools to pretend a wasm module is an ES6 module at the moment but those should be coming soon! In the meantime a local `wasm2es6js` hack was added to help make *something* work today. The README has also been updated with instructions for interacting with this model.
2018-01-29 21:20:38 -08:00
typescript: bool,
2018-04-11 11:43:18 -07:00
demangle: bool,
keep_debug: bool,
2017-12-14 19:31:01 -08:00
}
enum Input {
Path(PathBuf),
Bytes(Vec<u8>, String),
Module(Module, String),
None,
}
2017-12-14 19:31:01 -08:00
impl Bindgen {
pub fn new() -> Bindgen {
Bindgen {
input: Input::None,
2017-12-14 21:55:21 -08:00
nodejs: false,
Speed up Travis by running Webpack in fewer tests (#381) * Reorganize Travis configuration * Add a `JOB` env var descriptor to all matrix entries. Not used anywhere but is useful when viewing the whole build on Travis's web interface. * Reorganize where builds are located, moving slow builds first and fast ones last. * Change checking the CLI builds from `cargo build` to `cargo check` * Use YAML references to reduce some duplication * Print some more timing statistics for each test * Extract `Project` helper in tests to a module This'll help make it a bit more extensible over time. At the same time the methods are also slightly reorganized to read more clearly from top to bottom. * Migrate all tests away from Webpack Wepback can take a significant amount of time to execute and when it's multiplied by hundreds of tests that adds up really quickly! After investigating Node's `--experimental-modules` option it looks like it's suitable for our use so this switches all tests to using JS files (moving away from TypeScript as well) with `--experimental-modules` with Node. Tests will be selectively re-enabled with webpack and node.js specific output (that doesn't require `--experimental-modules`), coming in later commits. * Restore the node test for node.js output Ensures it's workable as-is * Only generate typescript with webpack * Only read wasm files for webpack * Skip package.json/node_modules for now * Only generate webpack config if needed * Start a dedicated test module for typescript Will hopefully verify the generated Typescript compiles OK. * Remove unneeded `node` method * Fixup some rebase conflicts * Don't run asmjs example on travis * Fixup generator tests * Attempt to fix windows * Comment windows fix * More test fixes * More exclusions * More test fixes * Relax eslint regex Catch mjs modules as well * Fix eslint * Speed up travis on examples slightly
2018-07-04 22:37:09 -05:00
nodejs_experimental_modules: false,
browser: false,
2018-04-11 13:59:58 +05:45
no_modules: false,
no_modules_global: None,
debug: false,
Rewrite wasm-bindgen with ES6 modules in mind This commit is a mostly-rewrite of the `wasm-bindgen` tool. After some recent discussions it's clear that the previous model wasn't quite going to cut it, and this iteration is one which primarily embraces ES6 modules and the idea that this is a polyfill for host bindings. The overall interface and functionality hasn't changed much but the underlying technology has now changed significantly. Previously `wasm-bindgen` would emit a JS file that acted as an ES6 module but had a bit of a wonky interface. It exposed an async function for instantiation of the wasm module, but that's the bundler's job, not ours! Instead this iteration views each input and output as a discrete ES6 module. The input wasm file is interpreted as "this *should* be an ES6 module with rich types" and the output is "well here's some ES6 modules that fulfill that contract". Notably the tool now replaces the original wasm ES6 module with a JS ES6 module that has the "rich interface". Additionally a second ES6 module is emitted (the actual wasm file) which imports and exports to the original ES6 module. This strategy is hoped to be much more amenable to bundlers and controlling how the wasm itself is instantiated. The emitted files files purely assume ES6 modules and should be able to work as-is once ES6 module integration for wasm is completed. Note that there aren't a ton of tools to pretend a wasm module is an ES6 module at the moment but those should be coming soon! In the meantime a local `wasm2es6js` hack was added to help make *something* work today. The README has also been updated with instructions for interacting with this model.
2018-01-29 21:20:38 -08:00
typescript: false,
2018-04-11 11:43:18 -07:00
demangle: true,
keep_debug: false,
2017-12-14 19:31:01 -08:00
}
}
pub fn input_path<P: AsRef<Path>>(&mut self, path: P) -> &mut Bindgen {
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);
2017-12-14 19:31:01 -08:00
self
}
2017-12-14 21:55:21 -08:00
pub fn nodejs(&mut self, node: bool) -> &mut Bindgen {
self.nodejs = node;
self
}
Speed up Travis by running Webpack in fewer tests (#381) * Reorganize Travis configuration * Add a `JOB` env var descriptor to all matrix entries. Not used anywhere but is useful when viewing the whole build on Travis's web interface. * Reorganize where builds are located, moving slow builds first and fast ones last. * Change checking the CLI builds from `cargo build` to `cargo check` * Use YAML references to reduce some duplication * Print some more timing statistics for each test * Extract `Project` helper in tests to a module This'll help make it a bit more extensible over time. At the same time the methods are also slightly reorganized to read more clearly from top to bottom. * Migrate all tests away from Webpack Wepback can take a significant amount of time to execute and when it's multiplied by hundreds of tests that adds up really quickly! After investigating Node's `--experimental-modules` option it looks like it's suitable for our use so this switches all tests to using JS files (moving away from TypeScript as well) with `--experimental-modules` with Node. Tests will be selectively re-enabled with webpack and node.js specific output (that doesn't require `--experimental-modules`), coming in later commits. * Restore the node test for node.js output Ensures it's workable as-is * Only generate typescript with webpack * Only read wasm files for webpack * Skip package.json/node_modules for now * Only generate webpack config if needed * Start a dedicated test module for typescript Will hopefully verify the generated Typescript compiles OK. * Remove unneeded `node` method * Fixup some rebase conflicts * Don't run asmjs example on travis * Fixup generator tests * Attempt to fix windows * Comment windows fix * More test fixes * More exclusions * More test fixes * Relax eslint regex Catch mjs modules as well * Fix eslint * Speed up travis on examples slightly
2018-07-04 22:37:09 -05:00
pub fn nodejs_experimental_modules(&mut self, node: bool) -> &mut Bindgen {
self.nodejs_experimental_modules = node;
self
}
pub fn browser(&mut self, browser: bool) -> &mut Bindgen {
self.browser = browser;
self
}
2018-04-11 13:59:58 +05:45
pub fn no_modules(&mut self, no_modules: bool) -> &mut Bindgen {
self.no_modules = no_modules;
2018-04-04 20:06:53 +05:45
self
}
pub fn no_modules_global(&mut self, name: &str) -> &mut Bindgen {
self.no_modules_global = Some(name.to_string());
self
}
pub fn debug(&mut self, debug: bool) -> &mut Bindgen {
self.debug = debug;
self
}
Rewrite wasm-bindgen with ES6 modules in mind This commit is a mostly-rewrite of the `wasm-bindgen` tool. After some recent discussions it's clear that the previous model wasn't quite going to cut it, and this iteration is one which primarily embraces ES6 modules and the idea that this is a polyfill for host bindings. The overall interface and functionality hasn't changed much but the underlying technology has now changed significantly. Previously `wasm-bindgen` would emit a JS file that acted as an ES6 module but had a bit of a wonky interface. It exposed an async function for instantiation of the wasm module, but that's the bundler's job, not ours! Instead this iteration views each input and output as a discrete ES6 module. The input wasm file is interpreted as "this *should* be an ES6 module with rich types" and the output is "well here's some ES6 modules that fulfill that contract". Notably the tool now replaces the original wasm ES6 module with a JS ES6 module that has the "rich interface". Additionally a second ES6 module is emitted (the actual wasm file) which imports and exports to the original ES6 module. This strategy is hoped to be much more amenable to bundlers and controlling how the wasm itself is instantiated. The emitted files files purely assume ES6 modules and should be able to work as-is once ES6 module integration for wasm is completed. Note that there aren't a ton of tools to pretend a wasm module is an ES6 module at the moment but those should be coming soon! In the meantime a local `wasm2es6js` hack was added to help make *something* work today. The README has also been updated with instructions for interacting with this model.
2018-01-29 21:20:38 -08:00
pub fn typescript(&mut self, typescript: bool) -> &mut Bindgen {
self.typescript = typescript;
self
}
2018-04-11 11:43:18 -07:00
pub fn demangle(&mut self, demangle: bool) -> &mut Bindgen {
self.demangle = demangle;
self
}
pub fn keep_debug(&mut self, keep_debug: bool) -> &mut Bindgen {
self.keep_debug = keep_debug;
self
}
Rewrite wasm-bindgen with ES6 modules in mind This commit is a mostly-rewrite of the `wasm-bindgen` tool. After some recent discussions it's clear that the previous model wasn't quite going to cut it, and this iteration is one which primarily embraces ES6 modules and the idea that this is a polyfill for host bindings. The overall interface and functionality hasn't changed much but the underlying technology has now changed significantly. Previously `wasm-bindgen` would emit a JS file that acted as an ES6 module but had a bit of a wonky interface. It exposed an async function for instantiation of the wasm module, but that's the bundler's job, not ours! Instead this iteration views each input and output as a discrete ES6 module. The input wasm file is interpreted as "this *should* be an ES6 module with rich types" and the output is "well here's some ES6 modules that fulfill that contract". Notably the tool now replaces the original wasm ES6 module with a JS ES6 module that has the "rich interface". Additionally a second ES6 module is emitted (the actual wasm file) which imports and exports to the original ES6 module. This strategy is hoped to be much more amenable to bundlers and controlling how the wasm itself is instantiated. The emitted files files purely assume ES6 modules and should be able to work as-is once ES6 module integration for wasm is completed. Note that there aren't a ton of tools to pretend a wasm module is an ES6 module at the moment but those should be coming soon! In the meantime a local `wasm2es6js` hack was added to help make *something* work today. The README has also been updated with instructions for interacting with this model.
2018-01-29 21:20:38 -08:00
pub fn generate<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
self._generate(path.as_ref())
}
fn _generate(&mut self, out_dir: &Path) -> Result<(), Error> {
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::<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)
}
2017-12-14 19:31:01 -08:00
};
let programs = extract_programs(&mut module)
.with_context(|_| "failed to extract wasm-bindgen custom sections")?;
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
// Here we're actually instantiating the module we've parsed above for
// execution. Why, you might be asking, are we executing wasm code? A
// good question!
//
// Transmitting information from `#[wasm_bindgen]` here to the CLI tool
// is pretty tricky. Specifically information about the types involved
// with a function signature (especially generic ones) can be hefty to
// translate over. As a result, the macro emits a bunch of shims which,
// when executed, will describe to us what the types look like.
//
// 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
// then generate the appropriate bindings.
//
// 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")?;
let instance = wasmi::ModuleInstance::new(&instance, &MyResolver)
.with_context(|_| "failed to instantiate wasm module")?;
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
let instance = instance.not_started_instance();
let (js, ts) = {
let mut cx = js::Context {
globals: String::new(),
imports: String::new(),
footer: String::new(),
typescript: format!("/* tslint:disable */\n"),
exposed_globals: Default::default(),
required_internal_exports: Default::default(),
imported_names: Default::default(),
exported_classes: Default::default(),
config: &self,
module: &mut module,
function_table_needed: false,
module_versions: Default::default(),
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
run_descriptor: &|name| {
let mut v = MyExternals(Vec::new());
match instance.invoke_export(name, &[], &mut v) {
Ok(None) => Some(v.0),
2018-06-27 22:42:34 -07:00
Ok(Some(_)) => unreachable!(
"there is only one export, and we only return None from it"
),
// Allow missing exported describe functions. This can
// happen when a nested dependency crate exports things
// but the root crate doesn't use them.
Err(wasmi::Error::Function(_)) => None,
Err(e) => panic!("unexpected error running descriptor: {}", e),
}
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
},
};
for program in programs.iter() {
js::SubContext {
program,
cx: &mut cx,
}.generate()?;
}
cx.finalize(stem)?
};
Rewrite wasm-bindgen with ES6 modules in mind This commit is a mostly-rewrite of the `wasm-bindgen` tool. After some recent discussions it's clear that the previous model wasn't quite going to cut it, and this iteration is one which primarily embraces ES6 modules and the idea that this is a polyfill for host bindings. The overall interface and functionality hasn't changed much but the underlying technology has now changed significantly. Previously `wasm-bindgen` would emit a JS file that acted as an ES6 module but had a bit of a wonky interface. It exposed an async function for instantiation of the wasm module, but that's the bundler's job, not ours! Instead this iteration views each input and output as a discrete ES6 module. The input wasm file is interpreted as "this *should* be an ES6 module with rich types" and the output is "well here's some ES6 modules that fulfill that contract". Notably the tool now replaces the original wasm ES6 module with a JS ES6 module that has the "rich interface". Additionally a second ES6 module is emitted (the actual wasm file) which imports and exports to the original ES6 module. This strategy is hoped to be much more amenable to bundlers and controlling how the wasm itself is instantiated. The emitted files files purely assume ES6 modules and should be able to work as-is once ES6 module integration for wasm is completed. Note that there aren't a ton of tools to pretend a wasm module is an ES6 module at the moment but those should be coming soon! In the meantime a local `wasm2es6js` hack was added to help make *something* work today. The README has also been updated with instructions for interacting with this model.
2018-01-29 21:20:38 -08:00
Speed up Travis by running Webpack in fewer tests (#381) * Reorganize Travis configuration * Add a `JOB` env var descriptor to all matrix entries. Not used anywhere but is useful when viewing the whole build on Travis's web interface. * Reorganize where builds are located, moving slow builds first and fast ones last. * Change checking the CLI builds from `cargo build` to `cargo check` * Use YAML references to reduce some duplication * Print some more timing statistics for each test * Extract `Project` helper in tests to a module This'll help make it a bit more extensible over time. At the same time the methods are also slightly reorganized to read more clearly from top to bottom. * Migrate all tests away from Webpack Wepback can take a significant amount of time to execute and when it's multiplied by hundreds of tests that adds up really quickly! After investigating Node's `--experimental-modules` option it looks like it's suitable for our use so this switches all tests to using JS files (moving away from TypeScript as well) with `--experimental-modules` with Node. Tests will be selectively re-enabled with webpack and node.js specific output (that doesn't require `--experimental-modules`), coming in later commits. * Restore the node test for node.js output Ensures it's workable as-is * Only generate typescript with webpack * Only read wasm files for webpack * Skip package.json/node_modules for now * Only generate webpack config if needed * Start a dedicated test module for typescript Will hopefully verify the generated Typescript compiles OK. * Remove unneeded `node` method * Fixup some rebase conflicts * Don't run asmjs example on travis * Fixup generator tests * Attempt to fix windows * Comment windows fix * More test fixes * More exclusions * More test fixes * Relax eslint regex Catch mjs modules as well * Fix eslint * Speed up travis on examples slightly
2018-07-04 22:37:09 -05:00
let extension = if self.nodejs_experimental_modules { "mjs" } else { "js" };
let js_path = out_dir.join(stem).with_extension(extension);
fs::write(&js_path, reset_indentation(&js))
.with_context(|_| format!("failed to write `{}`", js_path.display()))?;
Rewrite wasm-bindgen with ES6 modules in mind This commit is a mostly-rewrite of the `wasm-bindgen` tool. After some recent discussions it's clear that the previous model wasn't quite going to cut it, and this iteration is one which primarily embraces ES6 modules and the idea that this is a polyfill for host bindings. The overall interface and functionality hasn't changed much but the underlying technology has now changed significantly. Previously `wasm-bindgen` would emit a JS file that acted as an ES6 module but had a bit of a wonky interface. It exposed an async function for instantiation of the wasm module, but that's the bundler's job, not ours! Instead this iteration views each input and output as a discrete ES6 module. The input wasm file is interpreted as "this *should* be an ES6 module with rich types" and the output is "well here's some ES6 modules that fulfill that contract". Notably the tool now replaces the original wasm ES6 module with a JS ES6 module that has the "rich interface". Additionally a second ES6 module is emitted (the actual wasm file) which imports and exports to the original ES6 module. This strategy is hoped to be much more amenable to bundlers and controlling how the wasm itself is instantiated. The emitted files files purely assume ES6 modules and should be able to work as-is once ES6 module integration for wasm is completed. Note that there aren't a ton of tools to pretend a wasm module is an ES6 module at the moment but those should be coming soon! In the meantime a local `wasm2es6js` hack was added to help make *something* work today. The README has also been updated with instructions for interacting with this model.
2018-01-29 21:20:38 -08:00
if self.typescript {
let ts_path = out_dir.join(stem).with_extension("d.ts");
fs::write(&ts_path, ts)
.with_context(|_| format!("failed to write `{}`", ts_path.display()))?;
Rewrite wasm-bindgen with ES6 modules in mind This commit is a mostly-rewrite of the `wasm-bindgen` tool. After some recent discussions it's clear that the previous model wasn't quite going to cut it, and this iteration is one which primarily embraces ES6 modules and the idea that this is a polyfill for host bindings. The overall interface and functionality hasn't changed much but the underlying technology has now changed significantly. Previously `wasm-bindgen` would emit a JS file that acted as an ES6 module but had a bit of a wonky interface. It exposed an async function for instantiation of the wasm module, but that's the bundler's job, not ours! Instead this iteration views each input and output as a discrete ES6 module. The input wasm file is interpreted as "this *should* be an ES6 module with rich types" and the output is "well here's some ES6 modules that fulfill that contract". Notably the tool now replaces the original wasm ES6 module with a JS ES6 module that has the "rich interface". Additionally a second ES6 module is emitted (the actual wasm file) which imports and exports to the original ES6 module. This strategy is hoped to be much more amenable to bundlers and controlling how the wasm itself is instantiated. The emitted files files purely assume ES6 modules and should be able to work as-is once ES6 module integration for wasm is completed. Note that there aren't a ton of tools to pretend a wasm module is an ES6 module at the moment but those should be coming soon! In the meantime a local `wasm2es6js` hack was added to help make *something* work today. The README has also been updated with instructions for interacting with this model.
2018-01-29 21:20:38 -08:00
}
2017-12-14 19:31:01 -08:00
let wasm_path = out_dir.join(format!("{}_bg", stem)).with_extension("wasm");
if self.nodejs {
Speed up Travis by running Webpack in fewer tests (#381) * Reorganize Travis configuration * Add a `JOB` env var descriptor to all matrix entries. Not used anywhere but is useful when viewing the whole build on Travis's web interface. * Reorganize where builds are located, moving slow builds first and fast ones last. * Change checking the CLI builds from `cargo build` to `cargo check` * Use YAML references to reduce some duplication * Print some more timing statistics for each test * Extract `Project` helper in tests to a module This'll help make it a bit more extensible over time. At the same time the methods are also slightly reorganized to read more clearly from top to bottom. * Migrate all tests away from Webpack Wepback can take a significant amount of time to execute and when it's multiplied by hundreds of tests that adds up really quickly! After investigating Node's `--experimental-modules` option it looks like it's suitable for our use so this switches all tests to using JS files (moving away from TypeScript as well) with `--experimental-modules` with Node. Tests will be selectively re-enabled with webpack and node.js specific output (that doesn't require `--experimental-modules`), coming in later commits. * Restore the node test for node.js output Ensures it's workable as-is * Only generate typescript with webpack * Only read wasm files for webpack * Skip package.json/node_modules for now * Only generate webpack config if needed * Start a dedicated test module for typescript Will hopefully verify the generated Typescript compiles OK. * Remove unneeded `node` method * Fixup some rebase conflicts * Don't run asmjs example on travis * Fixup generator tests * Attempt to fix windows * Comment windows fix * More test fixes * More exclusions * More test fixes * Relax eslint regex Catch mjs modules as well * Fix eslint * Speed up travis on examples slightly
2018-07-04 22:37:09 -05:00
let js_path = wasm_path.with_extension(extension);
let shim = self.generate_node_wasm_import(&module, &wasm_path);
fs::write(&js_path, shim)
.with_context(|_| format!("failed to write `{}`", js_path.display()))?;
}
let wasm_bytes = parity_wasm::serialize(module)?;
fs::write(&wasm_path, wasm_bytes)
.with_context(|_| format!("failed to write `{}`", wasm_path.display()))?;
2017-12-14 19:31:01 -08:00
Ok(())
}
fn generate_node_wasm_import(&self, m: &Module, path: &Path) -> String {
let mut imports = BTreeSet::new();
if let Some(i) = m.import_section() {
for i in i.entries() {
imports.insert(i.module());
}
}
let mut shim = String::new();
Speed up Travis by running Webpack in fewer tests (#381) * Reorganize Travis configuration * Add a `JOB` env var descriptor to all matrix entries. Not used anywhere but is useful when viewing the whole build on Travis's web interface. * Reorganize where builds are located, moving slow builds first and fast ones last. * Change checking the CLI builds from `cargo build` to `cargo check` * Use YAML references to reduce some duplication * Print some more timing statistics for each test * Extract `Project` helper in tests to a module This'll help make it a bit more extensible over time. At the same time the methods are also slightly reorganized to read more clearly from top to bottom. * Migrate all tests away from Webpack Wepback can take a significant amount of time to execute and when it's multiplied by hundreds of tests that adds up really quickly! After investigating Node's `--experimental-modules` option it looks like it's suitable for our use so this switches all tests to using JS files (moving away from TypeScript as well) with `--experimental-modules` with Node. Tests will be selectively re-enabled with webpack and node.js specific output (that doesn't require `--experimental-modules`), coming in later commits. * Restore the node test for node.js output Ensures it's workable as-is * Only generate typescript with webpack * Only read wasm files for webpack * Skip package.json/node_modules for now * Only generate webpack config if needed * Start a dedicated test module for typescript Will hopefully verify the generated Typescript compiles OK. * Remove unneeded `node` method * Fixup some rebase conflicts * Don't run asmjs example on travis * Fixup generator tests * Attempt to fix windows * Comment windows fix * More test fixes * More exclusions * More test fixes * Relax eslint regex Catch mjs modules as well * Fix eslint * Speed up travis on examples slightly
2018-07-04 22:37:09 -05:00
if self.nodejs_experimental_modules {
for (i, module) in imports.iter().enumerate() {
shim.push_str(&format!("import * as import{} from '{}';\n",
i, module));
}
// On windows skip the leading `/` which comes out when we parse a
// url to use `C:\...` instead of `\C:\...`
shim.push_str(&format!("
import * as path from 'path';
import * as fs from 'fs';
import * as url from 'url';
import * as process from 'process';
let file = path.dirname(url.parse(import.meta.url).pathname);
if (process.platform === 'win32') {{
file = file.substring(1);
}}
const bytes = fs.readFileSync(path.join(file, '{}'));
", path.file_name().unwrap().to_str().unwrap()));
} else {
shim.push_str(&format!("
const path = require('path').join(__dirname, '{}');
const bytes = require('fs').readFileSync(path);
", path.file_name().unwrap().to_str().unwrap()));
}
shim.push_str("let imports = {};\n");
Speed up Travis by running Webpack in fewer tests (#381) * Reorganize Travis configuration * Add a `JOB` env var descriptor to all matrix entries. Not used anywhere but is useful when viewing the whole build on Travis's web interface. * Reorganize where builds are located, moving slow builds first and fast ones last. * Change checking the CLI builds from `cargo build` to `cargo check` * Use YAML references to reduce some duplication * Print some more timing statistics for each test * Extract `Project` helper in tests to a module This'll help make it a bit more extensible over time. At the same time the methods are also slightly reorganized to read more clearly from top to bottom. * Migrate all tests away from Webpack Wepback can take a significant amount of time to execute and when it's multiplied by hundreds of tests that adds up really quickly! After investigating Node's `--experimental-modules` option it looks like it's suitable for our use so this switches all tests to using JS files (moving away from TypeScript as well) with `--experimental-modules` with Node. Tests will be selectively re-enabled with webpack and node.js specific output (that doesn't require `--experimental-modules`), coming in later commits. * Restore the node test for node.js output Ensures it's workable as-is * Only generate typescript with webpack * Only read wasm files for webpack * Skip package.json/node_modules for now * Only generate webpack config if needed * Start a dedicated test module for typescript Will hopefully verify the generated Typescript compiles OK. * Remove unneeded `node` method * Fixup some rebase conflicts * Don't run asmjs example on travis * Fixup generator tests * Attempt to fix windows * Comment windows fix * More test fixes * More exclusions * More test fixes * Relax eslint regex Catch mjs modules as well * Fix eslint * Speed up travis on examples slightly
2018-07-04 22:37:09 -05:00
for (i, module) in imports.iter().enumerate() {
if self.nodejs_experimental_modules {
shim.push_str(&format!("imports['{}'] = import{};\n", module, i));
} else {
shim.push_str(&format!("imports['{0}'] = require('{0}');\n", module));
}
}
2018-06-27 22:42:34 -07:00
shim.push_str(&format!(
"
Speed up Travis by running Webpack in fewer tests (#381) * Reorganize Travis configuration * Add a `JOB` env var descriptor to all matrix entries. Not used anywhere but is useful when viewing the whole build on Travis's web interface. * Reorganize where builds are located, moving slow builds first and fast ones last. * Change checking the CLI builds from `cargo build` to `cargo check` * Use YAML references to reduce some duplication * Print some more timing statistics for each test * Extract `Project` helper in tests to a module This'll help make it a bit more extensible over time. At the same time the methods are also slightly reorganized to read more clearly from top to bottom. * Migrate all tests away from Webpack Wepback can take a significant amount of time to execute and when it's multiplied by hundreds of tests that adds up really quickly! After investigating Node's `--experimental-modules` option it looks like it's suitable for our use so this switches all tests to using JS files (moving away from TypeScript as well) with `--experimental-modules` with Node. Tests will be selectively re-enabled with webpack and node.js specific output (that doesn't require `--experimental-modules`), coming in later commits. * Restore the node test for node.js output Ensures it's workable as-is * Only generate typescript with webpack * Only read wasm files for webpack * Skip package.json/node_modules for now * Only generate webpack config if needed * Start a dedicated test module for typescript Will hopefully verify the generated Typescript compiles OK. * Remove unneeded `node` method * Fixup some rebase conflicts * Don't run asmjs example on travis * Fixup generator tests * Attempt to fix windows * Comment windows fix * More test fixes * More exclusions * More test fixes * Relax eslint regex Catch mjs modules as well * Fix eslint * Speed up travis on examples slightly
2018-07-04 22:37:09 -05:00
const wasmModule = new WebAssembly.Module(bytes);
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
",
2018-06-27 22:42:34 -07:00
));
Speed up Travis by running Webpack in fewer tests (#381) * Reorganize Travis configuration * Add a `JOB` env var descriptor to all matrix entries. Not used anywhere but is useful when viewing the whole build on Travis's web interface. * Reorganize where builds are located, moving slow builds first and fast ones last. * Change checking the CLI builds from `cargo build` to `cargo check` * Use YAML references to reduce some duplication * Print some more timing statistics for each test * Extract `Project` helper in tests to a module This'll help make it a bit more extensible over time. At the same time the methods are also slightly reorganized to read more clearly from top to bottom. * Migrate all tests away from Webpack Wepback can take a significant amount of time to execute and when it's multiplied by hundreds of tests that adds up really quickly! After investigating Node's `--experimental-modules` option it looks like it's suitable for our use so this switches all tests to using JS files (moving away from TypeScript as well) with `--experimental-modules` with Node. Tests will be selectively re-enabled with webpack and node.js specific output (that doesn't require `--experimental-modules`), coming in later commits. * Restore the node test for node.js output Ensures it's workable as-is * Only generate typescript with webpack * Only read wasm files for webpack * Skip package.json/node_modules for now * Only generate webpack config if needed * Start a dedicated test module for typescript Will hopefully verify the generated Typescript compiles OK. * Remove unneeded `node` method * Fixup some rebase conflicts * Don't run asmjs example on travis * Fixup generator tests * Attempt to fix windows * Comment windows fix * More test fixes * More exclusions * More test fixes * Relax eslint regex Catch mjs modules as well * Fix eslint * Speed up travis on examples slightly
2018-07-04 22:37:09 -05:00
if self.nodejs_experimental_modules {
if let Some(e) = m.export_section() {
for name in e.entries().iter().map(|e| e.field()) {
shim.push_str("export const ");
shim.push_str(name);
shim.push_str(" = wasmInstance.exports.");
shim.push_str(name);
shim.push_str(";\n");
}
}
} else {
shim.push_str("module.exports = wasmInstance.exports;\n");
}
2018-06-15 12:55:37 -05:00
reset_indentation(&shim)
}
2017-12-14 19:31:01 -08:00
}
fn extract_programs(module: &mut Module) -> Result<Vec<shared::Program>, Error> {
let version = shared::version();
let mut ret = Vec::new();
let mut to_remove = Vec::new();
2017-12-14 19:31:01 -08:00
for (i, s) in module.sections().iter().enumerate() {
let custom = match *s {
Section::Custom(ref s) => s,
_ => continue,
};
if custom.name() != "__wasm_bindgen_unstable" {
2018-06-27 22:42:34 -07:00
continue;
}
to_remove.push(i);
Start removal of vector special-casing This commit starts wasm-bindgen down a path of removing the special casing it currently has around vectors, slices, and strings. This has long been a thorn in wasm-bindgen's side as it doesn't handle other kinds of vectors and otherwise is very inflexible with future additions. Additionally it leads to a lot of duplicated-ish code throughout various portions of codegen. The fundamental reason for this was that two arguments were required to be passed back to wasm, and I couldn't figure out a way to shove both those arguments into a function argument. The new strategy here is that there is one global stack well known to both JS and Rust which arguments *may* also be transferred between. By default all ABI arguments pass as literal function arguments, but if two or more arguments need to be passed then the extra ones are all passed through this global stack. The stack is effectively temporary scratch space when crossing the JS/Rust boundary (both ways). No long term storage is intended here. The `simple` test is passing as a result of this commit, using strings internally. The `Vector` type in the AST has been removed (yay!) and the bulk of the implementation of slices and vectors now resides in the `wasm-bindgen` crate itself, defining how to pass all these arguments around. The JS generator, however, still needs to know about all the sorts of vectors so it can generate appropriate code for JS. Future commits will continue cleanup and get the rest of the tests working.
2018-03-31 07:57:47 -07:00
let mut payload = custom.payload();
let mut added_programs = Vec::new();
while payload.len() > 0 {
2018-06-27 22:42:34 -07:00
let len = ((payload[0] as usize) << 0)
| ((payload[1] as usize) << 8)
| ((payload[2] as usize) << 16)
| ((payload[3] as usize) << 24);
Start removal of vector special-casing This commit starts wasm-bindgen down a path of removing the special casing it currently has around vectors, slices, and strings. This has long been a thorn in wasm-bindgen's side as it doesn't handle other kinds of vectors and otherwise is very inflexible with future additions. Additionally it leads to a lot of duplicated-ish code throughout various portions of codegen. The fundamental reason for this was that two arguments were required to be passed back to wasm, and I couldn't figure out a way to shove both those arguments into a function argument. The new strategy here is that there is one global stack well known to both JS and Rust which arguments *may* also be transferred between. By default all ABI arguments pass as literal function arguments, but if two or more arguments need to be passed then the extra ones are all passed through this global stack. The stack is effectively temporary scratch space when crossing the JS/Rust boundary (both ways). No long term storage is intended here. The `simple` test is passing as a result of this commit, using strings internally. The `Vector` type in the AST has been removed (yay!) and the bulk of the implementation of slices and vectors now resides in the `wasm-bindgen` crate itself, defining how to pass all these arguments around. The JS generator, however, still needs to know about all the sorts of vectors so it can generate appropriate code for JS. Future commits will continue cleanup and get the rest of the tests working.
2018-03-31 07:57:47 -07:00
let (a, b) = payload[4..].split_at(len as usize);
payload = b;
// Due to a nasty LLVM bug it's currently possible for LLVM to
// duplicate custom section directives in intermediate object files.
// This means that we could see multiple program directives when in
// fact we were originally only meant to see one!
//
// Work around the issue here until the upstream bug,
// https://bugs.llvm.org/show_bug.cgi?id=38184, is hopefully fixed
// via some other means.
if added_programs.iter().any(|p| a == *p) {
continue
}
added_programs.push(a);
let p: shared::ProgramOnlySchema = match serde_json::from_slice(&a) {
2017-12-14 19:31:01 -08:00
Ok(f) => f,
2018-06-27 22:42:34 -07:00
Err(e) => bail!("failed to decode what looked like wasm-bindgen data: {}", e),
2017-12-14 19:31:01 -08:00
};
if p.schema_version != shared::SCHEMA_VERSION {
2018-06-27 22:42:34 -07:00
bail!(
"
it looks like the Rust project used to create this wasm file was linked against
a different version of wasm-bindgen than this binary:
rust wasm file: {}
this binary: {}
Currently the bindgen format is unstable enough that these two version must
exactly match, so it's required that these two version are kept in sync by
either updating the wasm-bindgen dependency or this binary. You should be able
to update the wasm-bindgen dependency with:
cargo update -p wasm-bindgen
or you can update the binary with
cargo install -f wasm-bindgen-cli
if this warning fails to go away though and you're not sure what to do feel free
to open an issue at https://github.com/rustwasm/wasm-bindgen/issues!
",
2018-06-27 22:42:34 -07:00
p.version,
version
);
}
let p: shared::Program = match serde_json::from_slice(&a) {
Ok(f) => f,
2018-06-27 22:42:34 -07:00
Err(e) => bail!("failed to decode what looked like wasm-bindgen data: {}", e),
};
ret.push(p);
}
}
for i in to_remove.into_iter().rev() {
module.sections_mut().remove(i);
}
Ok(ret)
}
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
struct MyResolver;
impl wasmi::ImportResolver for MyResolver {
fn resolve_func(
&self,
module_name: &str,
field_name: &str,
2018-06-27 22:42:34 -07:00
signature: &wasmi::Signature,
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
) -> Result<wasmi::FuncRef, wasmi::Error> {
// Route our special "describe" export to 1 and everything else to 0.
// That way whenever the function 1 is invoked we know what to do and
// when 0 is invoked (by accident) we'll trap and produce an error.
2018-06-27 22:42:34 -07:00
let idx = (module_name == "__wbindgen_placeholder__" && field_name == "__wbindgen_describe")
as usize;
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
Ok(wasmi::FuncInstance::alloc_host(signature.clone(), idx))
}
fn resolve_global(
&self,
_module_name: &str,
_field_name: &str,
2018-06-27 22:42:34 -07:00
descriptor: &wasmi::GlobalDescriptor,
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
) -> Result<wasmi::GlobalRef, wasmi::Error> {
// dummy implementation to ensure instantiation succeeds
let val = match descriptor.value_type() {
wasmi::ValueType::I32 => wasmi::RuntimeValue::I32(0),
wasmi::ValueType::I64 => wasmi::RuntimeValue::I64(0),
2018-04-26 18:39:08 -07:00
wasmi::ValueType::F32 => wasmi::RuntimeValue::F32(0.0.into()),
wasmi::ValueType::F64 => wasmi::RuntimeValue::F64(0.0.into()),
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
};
Ok(wasmi::GlobalInstance::alloc(val, descriptor.is_mutable()))
}
fn resolve_memory(
&self,
_module_name: &str,
_field_name: &str,
descriptor: &wasmi::MemoryDescriptor,
) -> Result<wasmi::MemoryRef, wasmi::Error> {
// dummy implementation to ensure instantiation succeeds
use wasmi::memory_units::Pages;
let initial = Pages(descriptor.initial() as usize);
let maximum = descriptor.maximum().map(|i| Pages(i as usize));
wasmi::MemoryInstance::alloc(initial, maximum)
}
fn resolve_table(
&self,
_module_name: &str,
_field_name: &str,
2018-06-27 22:42:34 -07:00
descriptor: &wasmi::TableDescriptor,
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
) -> Result<wasmi::TableRef, wasmi::Error> {
// dummy implementation to ensure instantiation succeeds
let initial = descriptor.initial();
let maximum = descriptor.maximum();
wasmi::TableInstance::alloc(initial, maximum)
}
}
struct MyExternals(Vec<u32>);
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
#[derive(Debug)]
struct MyError(String);
impl wasmi::Externals for MyExternals {
fn invoke_index(
&mut self,
index: usize,
2018-06-27 22:42:34 -07:00
args: wasmi::RuntimeArgs,
Overhaul how type information gets to the CLI This commit is a complete overhaul of how the `#[wasm_bindgen]` macro communicates type information to the CLI tool, and it's done in a somewhat... unconventional fashion. Today we've got a problem where the generated JS needs to understand the types of each function exported or imported. This understanding is what enables it to generate the appropriate JS wrappers and such. We want to, however, be quite flexible and extensible in types that are supported across the boundary, which means that internally we rely on the trait system to resolve what's what. Communicating the type information historically was done by creating a four byte "descriptor" and using associated type projections to communicate that to the CLI tool. Unfortunately four bytes isn't a lot of space to cram information like arguments to a generic function, tuple types, etc. In general this just wasn't flexible enough and the way custom references were treated was also already a bit of a hack. This commit takes a radical step of creating a **descriptor function** for each function imported/exported. The really crazy part is that the `wasm-bindgen` CLI tool now embeds a wasm interpreter and executes these functions when the CLI tool is invoked. By allowing arbitrary functions to get executed it's now *much* easier to inform `wasm-bindgen` about complicated structures of types. Rest assured though that all these descriptor functions are automatically unexported and gc'd away, so this should not have any impact on binary sizes A new internal trait, `WasmDescribe`, is added to represent a description of all types, sort of like a serialization of the structure of a type that `wasm-bindgen` can understand. This works by calling a special exported function with a `u32` value a bunch of times. This means that when we run a descriptor we effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of integers can then be parsed into a rich `enum` for the JS generation to work with. This commit currently only retains feature parity with the previous implementation. I hope to soon solve issues like #123, #104, and #111 with this support.
2018-04-13 07:33:46 -07:00
) -> Result<Option<wasmi::RuntimeValue>, wasmi::Trap> {
macro_rules! bail {
($($t:tt)*) => ({
let s = MyError(format!($($t)*));
return Err(wasmi::Trap::new(wasmi::TrapKind::Host(Box::new(s))))
})
}
// We only recognize one function here which was mapped to the index 1
// by the resolver above.
if index != 1 {
bail!("only __wbindgen_describe can be run at this time")
}
if args.len() != 1 {
bail!("must have exactly one argument");
}
match args.nth_value_checked(0)? {
wasmi::RuntimeValue::I32(i) => self.0.push(i as u32),
_ => bail!("expected one argument of i32 type"),
}
Ok(None)
}
}
impl wasmi::HostError for MyError {}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
2018-06-15 12:55:37 -05:00
fn reset_indentation(s: &str) -> String {
let mut indent: u32 = 0;
let mut dst = String::new();
2018-06-15 12:55:37 -05:00
for line in s.lines() {
let line = line.trim();
2018-07-09 14:55:25 -05:00
if line.starts_with('}') || (line.ends_with('}') && !line.starts_with('*')) {
indent = indent.saturating_sub(1);
2018-06-15 12:55:37 -05:00
}
let extra = if line.starts_with(':') || line.starts_with('?') { 1 } else { 0 };
for _ in 0..indent + extra {
dst.push_str(" ");
2018-06-15 12:55:37 -05:00
}
dst.push_str(line);
dst.push_str("\n");
if line.ends_with('{') {
indent += 1;
2018-06-15 12:55:37 -05:00
}
}
return dst
2018-06-15 12:55:37 -05:00
}