Iteration order of hash maps is nondeterministic, so add a `sorted_iter`
function and then use that throughout whenever iteration order of a hash
map would affect the generated JS.
This commit reimplements the `anyref` transformation pass tasked with
taking raw rustc output and enhancing the module to use `anyref`. This
was disabled in the previous commits during refactoring, and now the
pass is re-enabled in the manner originally intended.
Instead of being tangled up in the `js/mod.rs` pass, the anyref
transformation now happens locally within one module,
`cli-support/src/anyref.rs`, which exclusively uses the output of the
`webidl` module which produces a WebIDL bindings section as well as an
auxiliary wasm-bindgen specific section. This makes the anyref transform
much more straightforward and local, ensuring that it doesn't propagate
elsewhere and can be a largely local concern during the transformation.
The main addition needed to support this pass was detailed knowledge of
the ABI of a `Descriptor`. This knowledge is already implicitly
hardcoded in `js2rust.rs` and `rust2js.rs` through the ABI shims
generated. This was previously used for the anyref transformation to
piggy-back what was already there, but as a separate pass we are unable
to reuse the knowledge in the binding generator.
Instead `Descriptor` now has two dedicated methods describing the
various ABI properties of a type. This is then asserted to be correct
(all the time) when processing bindings, ensuring that the two are kept
in sync.
This is no longe rneeded now that we precisely track what needs to be
exported for an imported item, so all the imports are hooked up
correctly elsewhere without the need for the `__exports` map.
This commit starts the `wasm-bindgen` CLI tool down the road to being a
true polyfill for WebIDL bindings. This refactor is probably the first
of a few, but is hopefully the largest and most sprawling and everything
will be a bit more targeted from here on out.
The goal of this refactoring is to separate out the massive
`crates/cli-support/src/js/mod.rs` into a number of separate pieces of
functionality. It currently takes care of basically everything
including:
* Binding intrinsics
* Handling anyref transformations
* Generating all JS for imports/exports
* All the logic for how to import and how to name imports
* Execution and management of wasm-bindgen closures
Many of these are separable concerns and most overlap with WebIDL
bindings. The internal refactoring here is intended to make it more
clear who's responsible for what as well as making some existing
operations much more straightforward. At a high-level, the following
changes are done:
1. A `src/webidl.rs` module is introduced. The purpose of this module is
to take all of the raw wasm-bindgen custom sections from the module
and transform them into a WebIDL bindings section.
This module has a placeholder `WebidlCustomSection` which is nowhere
near the actual custom section but if you squint is in theory very
similar. It's hoped that this will eventually become the true WebIDL
custom section, currently being developed in an external crate.
Currently, however, the WebIDL bindings custom section only covers a
subset of the functionality we export to wasm-bindgen users. To avoid
leaving them high and dry this module also contains an auxiliary
custom section named `WasmBindgenAux`. This custom section isn't
intended to have a binary format, but is intended to represent a
theoretical custom section necessary to couple with WebIDL bindings to
achieve all our desired functionality in `wasm-bindgen`. It'll never
be standardized, but it'll also never be serialized :)
2. The `src/webidl.rs` module now takes over quite a bit of
functionality from `src/js/mod.rs`. Namely it handles synthesis of an
`export_map` and an `import_map` mapping export/import IDs to exactly
what's expected to be hooked up there. This does not include type
information (as that's in the bindings section) but rather includes
things like "this is the method of class A" or "this import is from
module `foo`" and things like that. These could arguably be subsumed
by future JS features as well, but that's for another time!
3. All handling of wasm-bindgen "descriptor functions" now happens in a
dedicated `src/descriptors.rs` module. The output of this module is
its own custom section (intended to be immediately consumed by the
WebIDL module) which is in theory what we want to ourselves emit one
day but rustc isn't capable of doing so right now.
4. Invocations and generations of imports are completely overhauled.
Using the `import_map` generated in the WebIDL step all imports are
now handled much more precisely in one location rather than
haphazardly throughout the module. This means we have precise
information about each import of the module and we only modify
exactly what we're looking at. This also vastly simplifies intrinsic
generation since it's all simply a codegen part of the `rust2js.rs`
module now.
5. Handling of direct imports which don't have a JS shim generated is
slightly different from before and is intended to be
future-compatible with WebIDL bindings in its full glory, but we'll
need to update it to handle cases for constructors and method calls
eventually as well.
6. Intrinsic definitions now live in their own file (`src/intrinsic.rs`)
and have a separated definition for their symbol name and signature.
The actual implementation of each intrinsic lives in `rust2js.rs`
There's a number of TODO items to finish before this merges. This
includes reimplementing the anyref pass and actually implementing import
maps for other targets. Those will come soon in follow-up commits, but
the entire `tests/wasm/main.rs` suite is currently passing and this
seems like a good checkpoint.
Some speed up numbers from my string-heavy WASM benchmarks:
- Firefox + encodeInto: +45%
- Chrome + encodeInto: +80%
- Firefox + encode: +29%
- Chrome + encode: +62%
Note that this helps specifically with case of lots of small ASCII strings, in case of large strings there is no measurable difference in either direction.
Run exports through the same identifier generation as imports to ensure
that everything gets a unique identifier and then just make sure all the
appropriate wires are hooked up when dealing with exports and imports.
Closes#1496
Currently the import object constructed for the `--target web` output
only ever includes the current module as an one of the modules included.
With `wasm-bindgen`'s optimization to import directly from modules,
however, it's possible to have more modules imported from in the
generated wasm file. This commit ensures that the imports are hooked up
in the `--target web` es6 emulation mode, ensuring there aren't
extraneous errors about import objects.
The last write accidentally wasn't accounted for in the returned length
of the string and we unfortunately don't have any test coverage of
`encodeInto` since it requires Firefox nightly right now (and doesn't
work in Node yet).
Closes#1436
This commit adds an intrinsics to the `wasm_bindgen` crate which
accesses the `WebAssembly.Table` which is the function table of the
module. Eventually the thinking is that a module would import its own
function table via native wasm functionality (via `anyref` and such),
but until that's implemented let's add a binding for it ourselves!
Closes#1427
Because of some incorrect use of `js.push_str(..)`, we could sometimes emit code
before the ES modules imports, which is syntactically invalid:
const __exports = {};
import { Thing } from '...'; // Syntax error!
This has been fixed by making sure that the correct `imports` or `imports_post`
string is built up. We now also assert that the `js` string is empty at the
location where we add imports if we're using ES modules.
This commit fixes the `init` function when passed a
`WebAssembly.Module`. Upon closer reading of the [spec] we see there's
two possible return values from `WebAssembly.instantiate`. If passed a
`Module`, it will return only the `Instance`. If passed a buffer source,
though, it'll return an object with the module/instance.
The fix here is to check the result value is an `Instance`, and if so
assume the input must have been a module so it's paired up in the
output.
Closes#1418
[spec]: http://webassembly.github.io/spec/js-api/index.html#webassembly-namespace
Instead of doubling the size on each iteration, use precise upper limit (3 * JS length) if the string turned out not to be ASCII-only. This results in maximum of 1 reallocation instead of O(log N).
Some dummy examples of what this would change:
- 1000 of ASCII chars: no change, allocates 1000 bytes and bails out.
- 1000 ASCII chars + 1 '😃': before allocated 1000 bytes and reallocated to 2000; now allocates 1000 bytes and reallocates to 1006.
- 1000 of '😃' chars: before allocated 1000 bytes, reallocated to 2000, finally reallocated again to 4000; now allocates 1000 bytes and reallocates to 4000 right away.
Related issue: #1313
Most of the CLI crates were already in the 2018 edition, and it turns
out that one of the macro crates was already in the 2018 edition so we
may as well move everything to the 2018 edition!
Always nice to remove those `extern crate` statements nowadays!
This commit also does a `cargo fmt --all` to make sure we're conforming
with style again.
Node.js doesn't currently implement `TextEncoder::encodeInto`. I've raised an upstream issue to add it - https://github.com/nodejs/node/issues/26904 - but it's likely to take some time and will be available only in new releases.
In the meanwhile, it's worth noting that Node.js already has `Buffer::write` which has pretty similar semantics, but doesn't require creating an intermediate view using `.subarray` and instead accepts pointer and length directly.
Also, Node.js has `Buffer::byteLength` helper which allows to efficiently retrieve an encoded byte length of a string upfront, and so allows us to avoid a loop with reallocations.
This change takes leverage of these methods by generating an additional Buffer-based view into the WASM memory and using it for string operations.
I'm seeing up to 35% increase in performance in string-heavy library benchmarks.
We have very few tests today so this starts to add the basics of a test
suite which compiles Cargo projects on-the-fly which will hopefully help
us bolster the amount of assertions we can make about the output.
This commit implements [RFC 8], which enables transitive and transparent
dependencies on NPM. The `module` attribute, when seen and not part of a
local JS snippet, triggers detection of a `package.json` next to
`Cargo.toml`. If found it will cause the `wasm-bindgen` CLI tool to load
and parse the `package.json` within each crate and then create a merged
`package.json` at the end.
[RFC 8]: https://github.com/rustwasm/rfcs/pull/8
We've always wanted this to be the deterministic, but usage of `HashMap`
for example can accidentally lead to non-determinism. Looks like one was
forgotten and the bindings were nondeterministic by accident as a
result!
This commit deprecates the `--web`, `--no-modules`, and `--nodejs` flags
in favor of one `--target` flag. The motivation for this commit is to be
consistent between `wasm-bindgen` and `wasm-pack` so documentation for
one is applicable for the other (so we don't have to document everywhere
what the translation is between flags). Additionally this should make it
a bit easier to add new targets (if necessary) in the future as it won't
add to the proliferation of flags.
For now the old flags (like `--web`) continue to be accepted, but
they'll be removed during the next set of breaking changes for
`wasm-bindgen`.
This allows subverting the checks and resolution performed by the
`module` attribute added as part of [RFC 6] and has been discussed in #1343.
Closes#1343
[RFC 6]: https://github.com/rustwasm/rfcs/pull/6