2019-12-03 11:16:44 -06:00
|
|
|
//! Support for generating a standard wasm interface types
|
|
|
|
//!
|
|
|
|
//! This module has all the necessary support for generating a full-fledged
|
|
|
|
//! standard wasm interface types section as defined by the `wit_walrus`
|
|
|
|
//! crate. This module also critically assumes that the WebAssembly module
|
|
|
|
//! being generated **must be standalone**. In this mode all sorts of features
|
|
|
|
//! supported by `#[wasm_bindgen]` aren't actually supported, such as closures,
|
|
|
|
//! imports of global js names, js getters/setters, exporting structs, etc.
|
|
|
|
//! These features may all eventually come to the standard bindings proposal,
|
|
|
|
//! but it will likely take some time. In the meantime this module simply focuses
|
|
|
|
//! on taking what's already a valid wasm module and letting it through with a
|
|
|
|
//! standard WebIDL custom section. All other modules generate an error during
|
|
|
|
//! this binding process.
|
|
|
|
//!
|
|
|
|
//! Note that when this function is called and used we're also not actually
|
|
|
|
//! generating any JS glue. Any JS glue currently generated is also invalid if
|
|
|
|
//! the module contains the wasm bindings section and it's actually respected.
|
|
|
|
|
|
|
|
use crate::wit::{AdapterId, AdapterJsImportKind, AdapterType, Instruction};
|
|
|
|
use crate::wit::{AdapterKind, NonstandardWitSection, WasmBindgenAux};
|
|
|
|
use crate::wit::{AuxExport, InstructionData};
|
|
|
|
use crate::wit::{AuxExportKind, AuxImport, AuxValue, JsImport, JsImportName};
|
|
|
|
use anyhow::{anyhow, bail, Context, Error};
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use walrus::Module;
|
|
|
|
|
Add tests for the interface types output of wasm-bindgen (#1898)
* Add tests for the interface types output of wasm-bindgen
This commit expands the test suite with assertions about the output of
the interface types pass in wasm-bindgen. The goal here is to actually
assert that we produce the right output and have a suite of reference
files to show how the interface types output is changing over time.
The `reference` test suite added in the previous PR has been updated to
work for interface types as well, generating `*.wit` file assertions
which are printed via the `wit-printer` crate on crates.io.
Along the way a number of bugs were fixed with the interface types
output, such as:
* Non-determinism in output caused by iteration of a `HashMap`
* Avoiding JS generation entirely in interface types mode, ensuring that
we don't export extraneous intrinsics that aren't otherwise needed.
* Fixing location of the stack pointer for modules where it's GC'd out.
It's now rooted in the aux section of wasm-bindgen so it's available
to later passes, like the multi-value pass.
* Interface types emission now works in debug mode, meaning the
`--release` flag is no longer required. This previously did not work
because the `__wbindgen_throw` intrinsic was required in debug mode.
This comes about because of the `malloc_failure` and `internal_error`
functions in the anyref pass. The purpose of these functions is to
signal fatal runtime errors, if any, in a way that's usable to the
user. For wasm interface types though we can replace calls to these
functions with `unreachable` to avoid needing to import the
intrinsic. This has the accidental side effect of making
`wasm_bindgen::throw_str` "just work" with wasm interface types by
aborting the program, but that's not actually entirely intended. It's
hoped that a split of a `wasm-bindgen-core` crate would solve this
issue for the future.
* Run the wasm interface types validator in tests
* Add more gc roots for adapter gc
* Improve stack pointer detection
The stack pointer is never initialized to zero, but some other mutable
globals are (TLS, thread ID, etc), so let's filter those out.
2019-12-04 15:19:48 -06:00
|
|
|
pub fn add(module: &mut Module) -> Result<(), Error> {
|
|
|
|
let nonstandard = module
|
|
|
|
.customs
|
|
|
|
.delete_typed::<NonstandardWitSection>()
|
|
|
|
.unwrap();
|
|
|
|
let aux = module.customs.delete_typed::<WasmBindgenAux>().unwrap();
|
2019-12-03 11:16:44 -06:00
|
|
|
let mut section = wit_walrus::WasmInterfaceTypes::default();
|
|
|
|
let WasmBindgenAux {
|
|
|
|
extra_typescript: _, // ignore this even if it's specified
|
|
|
|
local_modules,
|
|
|
|
snippets,
|
|
|
|
package_jsons,
|
|
|
|
export_map,
|
|
|
|
import_map,
|
|
|
|
imports_with_catch,
|
|
|
|
imports_with_variadic,
|
|
|
|
imports_with_assert_no_shim: _, // not relevant for this purpose
|
|
|
|
enums,
|
|
|
|
structs,
|
Add tests for the interface types output of wasm-bindgen (#1898)
* Add tests for the interface types output of wasm-bindgen
This commit expands the test suite with assertions about the output of
the interface types pass in wasm-bindgen. The goal here is to actually
assert that we produce the right output and have a suite of reference
files to show how the interface types output is changing over time.
The `reference` test suite added in the previous PR has been updated to
work for interface types as well, generating `*.wit` file assertions
which are printed via the `wit-printer` crate on crates.io.
Along the way a number of bugs were fixed with the interface types
output, such as:
* Non-determinism in output caused by iteration of a `HashMap`
* Avoiding JS generation entirely in interface types mode, ensuring that
we don't export extraneous intrinsics that aren't otherwise needed.
* Fixing location of the stack pointer for modules where it's GC'd out.
It's now rooted in the aux section of wasm-bindgen so it's available
to later passes, like the multi-value pass.
* Interface types emission now works in debug mode, meaning the
`--release` flag is no longer required. This previously did not work
because the `__wbindgen_throw` intrinsic was required in debug mode.
This comes about because of the `malloc_failure` and `internal_error`
functions in the anyref pass. The purpose of these functions is to
signal fatal runtime errors, if any, in a way that's usable to the
user. For wasm interface types though we can replace calls to these
functions with `unreachable` to avoid needing to import the
intrinsic. This has the accidental side effect of making
`wasm_bindgen::throw_str` "just work" with wasm interface types by
aborting the program, but that's not actually entirely intended. It's
hoped that a split of a `wasm-bindgen-core` crate would solve this
issue for the future.
* Run the wasm interface types validator in tests
* Add more gc roots for adapter gc
* Improve stack pointer detection
The stack pointer is never initialized to zero, but some other mutable
globals are (TLS, thread ID, etc), so let's filter those out.
2019-12-04 15:19:48 -06:00
|
|
|
|
|
|
|
// irrelevant ids used to track various internal intrinsics and such
|
|
|
|
anyref_table: _,
|
|
|
|
anyref_alloc: _,
|
|
|
|
anyref_drop_slice: _,
|
|
|
|
exn_store: _,
|
|
|
|
shadow_stack_pointer: _,
|
|
|
|
} = *aux;
|
2019-12-03 11:16:44 -06:00
|
|
|
|
|
|
|
let adapter_context = |id: AdapterId| {
|
|
|
|
if let Some((name, _)) = nonstandard.exports.iter().find(|p| p.1 == id) {
|
|
|
|
return format!("in function export `{}`", name);
|
|
|
|
}
|
Add reference output tests for JS operations (#1894)
* Add reference output tests for JS operations
This commit starts adding a test suite which checks in, to the
repository, test assertions for both the JS and wasm file outputs of a
Rust crate compiled with `#[wasm_bindgen]`. These aren't intended to be
exhaustive or large scale tests, but rather micro-tests to help observe
the changes in `wasm-bindgen`'s output over time.
The motivation for this commit is basically overhauling how all the GC
passes work in `wasm-bindgen` today. The reorganization is also included
in this commit as well.
Previously `wasm-bindgen` would, in an ad-hoc fashion, run the GC passes
of `walrus` in a bunch of places to ensure that less "garbage" was seen
by future passes. This not only was a source of slowdown but it also was
pretty brittle since `wasm-bindgen` kept breaking if extra iteams leaked
through.
The strategy taken in this commit is to have one precise location for a
GC pass, and everything goes through there. This is achieved by:
* All internal exports are removed immediately when generating the
nonstandard wasm interface types section. Internal exports,
intrinsics, and runtime support are all referenced by the various
instructions and/or sections that use them. This means that we now
have precise tracking of what an adapter uses.
* This in turn enables us to implement the `add_gc_roots` function for
`walrus` custom sections, which in turn allows walrus GC passes to do
what `unexport_unused_intrinsics` did before. That function is now no
longer necessary, but effectively works the same way. All intrinsics
are unexported at the beginning and then they're selectively
re-imported and re-exported through the JS glue generation pass as
necessary and defined by the bindings.
* Passes like the `anyref` pass are now much more precise about the
intrinsics that they work with. The `anyref` pass also deletes any
internal intrinsics found and also does some rewriting of the adapters
aftewards now to hook up calls to the heap count import to the heap
count intrinsic in the wasm module.
* Fix handling of __wbindgen_realloc
The final user of the `require_internal_export` function was
`__wbindgen_realloc`. This usage has now been removed by updating how we
handle usage of the `realloc` function.
The wasm interface types standard doesn't have a `realloc` function
slot, nor do I think it ever will. This means that as a polyfill for
wasm interface types we'll always have to support the lack of `realloc`.
For direct Rust to JS, however, we can still optionally handle
`realloc`. This is all handled with a few internal changes.
* Custom `StringToMemory` instructions now exist. These have an extra
`realloc` slot to store an intrinsic, if found.
* Our custom instructions are lowered to the standard instructions when
generating an interface types section.
* The `realloc` function, if present, is passed as an argument like the
malloc function when passing strings to wasm. If it's not present we
use a slower fallback, but if it's present we use the faster
implementation.
This should mean that there's little-to-no impact on existing users of
`wasm-bindgen`, but this should continue to still work for wasm
interface types polyfills and such. Additionally the GC passes now work
in that they don't delete `__wbindgen_realloc` which we later try to
reference.
* Add an empty test for the anyref pass
* Precisely track I32FromOptionAnyref's dependencies
This depends on the anyref table and a function to allocate an index if
the anyref pass is running, so be sure to track that in the instruction
itself for GC rooting.
* Trim extraneous exports from nop anyref module
Or if you're otherwise not using anyref slices, don't force some
intrinsics to exist.
* Remove globals from reference tests
Looks like these values adjust in slight but insignificant ways over
time
* Update the anyref xform tests
2019-12-04 12:01:39 -06:00
|
|
|
if let Some((core, _, _)) = nonstandard.implements.iter().find(|p| p.2 == id) {
|
2019-12-03 11:16:44 -06:00
|
|
|
let import = module.imports.get(*core);
|
|
|
|
return format!(
|
|
|
|
"in function import from `{}::{}`",
|
|
|
|
import.module, import.name
|
|
|
|
);
|
|
|
|
}
|
Add tests for the interface types output of wasm-bindgen (#1898)
* Add tests for the interface types output of wasm-bindgen
This commit expands the test suite with assertions about the output of
the interface types pass in wasm-bindgen. The goal here is to actually
assert that we produce the right output and have a suite of reference
files to show how the interface types output is changing over time.
The `reference` test suite added in the previous PR has been updated to
work for interface types as well, generating `*.wit` file assertions
which are printed via the `wit-printer` crate on crates.io.
Along the way a number of bugs were fixed with the interface types
output, such as:
* Non-determinism in output caused by iteration of a `HashMap`
* Avoiding JS generation entirely in interface types mode, ensuring that
we don't export extraneous intrinsics that aren't otherwise needed.
* Fixing location of the stack pointer for modules where it's GC'd out.
It's now rooted in the aux section of wasm-bindgen so it's available
to later passes, like the multi-value pass.
* Interface types emission now works in debug mode, meaning the
`--release` flag is no longer required. This previously did not work
because the `__wbindgen_throw` intrinsic was required in debug mode.
This comes about because of the `malloc_failure` and `internal_error`
functions in the anyref pass. The purpose of these functions is to
signal fatal runtime errors, if any, in a way that's usable to the
user. For wasm interface types though we can replace calls to these
functions with `unreachable` to avoid needing to import the
intrinsic. This has the accidental side effect of making
`wasm_bindgen::throw_str` "just work" with wasm interface types by
aborting the program, but that's not actually entirely intended. It's
hoped that a split of a `wasm-bindgen-core` crate would solve this
issue for the future.
* Run the wasm interface types validator in tests
* Add more gc roots for adapter gc
* Improve stack pointer detection
The stack pointer is never initialized to zero, but some other mutable
globals are (TLS, thread ID, etc), so let's filter those out.
2019-12-04 15:19:48 -06:00
|
|
|
format!("in adapter function")
|
2019-12-03 11:16:44 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut us2walrus = HashMap::new();
|
Add tests for the interface types output of wasm-bindgen (#1898)
* Add tests for the interface types output of wasm-bindgen
This commit expands the test suite with assertions about the output of
the interface types pass in wasm-bindgen. The goal here is to actually
assert that we produce the right output and have a suite of reference
files to show how the interface types output is changing over time.
The `reference` test suite added in the previous PR has been updated to
work for interface types as well, generating `*.wit` file assertions
which are printed via the `wit-printer` crate on crates.io.
Along the way a number of bugs were fixed with the interface types
output, such as:
* Non-determinism in output caused by iteration of a `HashMap`
* Avoiding JS generation entirely in interface types mode, ensuring that
we don't export extraneous intrinsics that aren't otherwise needed.
* Fixing location of the stack pointer for modules where it's GC'd out.
It's now rooted in the aux section of wasm-bindgen so it's available
to later passes, like the multi-value pass.
* Interface types emission now works in debug mode, meaning the
`--release` flag is no longer required. This previously did not work
because the `__wbindgen_throw` intrinsic was required in debug mode.
This comes about because of the `malloc_failure` and `internal_error`
functions in the anyref pass. The purpose of these functions is to
signal fatal runtime errors, if any, in a way that's usable to the
user. For wasm interface types though we can replace calls to these
functions with `unreachable` to avoid needing to import the
intrinsic. This has the accidental side effect of making
`wasm_bindgen::throw_str` "just work" with wasm interface types by
aborting the program, but that's not actually entirely intended. It's
hoped that a split of a `wasm-bindgen-core` crate would solve this
issue for the future.
* Run the wasm interface types validator in tests
* Add more gc roots for adapter gc
* Improve stack pointer detection
The stack pointer is never initialized to zero, but some other mutable
globals are (TLS, thread ID, etc), so let's filter those out.
2019-12-04 15:19:48 -06:00
|
|
|
for (us, func) in crate::sorted_iter(&nonstandard.adapters) {
|
2019-12-03 11:16:44 -06:00
|
|
|
if let Some(export) = export_map.get(us) {
|
|
|
|
check_standard_export(export).context(adapter_context(*us))?;
|
|
|
|
}
|
|
|
|
if let Some(import) = import_map.get(us) {
|
|
|
|
check_standard_import(import).context(adapter_context(*us))?;
|
|
|
|
}
|
|
|
|
let params = translate_tys(&func.params).context(adapter_context(*us))?;
|
|
|
|
let results = translate_tys(&func.results).context(adapter_context(*us))?;
|
|
|
|
let ty = section.types.add(params, results);
|
|
|
|
let walrus = match &func.kind {
|
|
|
|
AdapterKind::Local { .. } => section.funcs.add_local(ty, Vec::new()),
|
|
|
|
AdapterKind::Import {
|
|
|
|
module,
|
|
|
|
name,
|
|
|
|
kind: AdapterJsImportKind::Normal,
|
|
|
|
} => section.add_import_func(module, name, ty).0,
|
|
|
|
AdapterKind::Import {
|
|
|
|
module,
|
|
|
|
name,
|
|
|
|
kind: AdapterJsImportKind::Constructor,
|
|
|
|
} => {
|
|
|
|
bail!(
|
|
|
|
"interfaces types doesn't support import of `{}::{}` \
|
|
|
|
as a constructor",
|
|
|
|
module,
|
|
|
|
name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
AdapterKind::Import {
|
|
|
|
module,
|
|
|
|
name,
|
|
|
|
kind: AdapterJsImportKind::Method,
|
|
|
|
} => {
|
|
|
|
bail!(
|
|
|
|
"interfaces types doesn't support import of `{}::{}` \
|
|
|
|
as a method",
|
|
|
|
module,
|
|
|
|
name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
us2walrus.insert(*us, walrus);
|
|
|
|
}
|
|
|
|
|
Add reference output tests for JS operations (#1894)
* Add reference output tests for JS operations
This commit starts adding a test suite which checks in, to the
repository, test assertions for both the JS and wasm file outputs of a
Rust crate compiled with `#[wasm_bindgen]`. These aren't intended to be
exhaustive or large scale tests, but rather micro-tests to help observe
the changes in `wasm-bindgen`'s output over time.
The motivation for this commit is basically overhauling how all the GC
passes work in `wasm-bindgen` today. The reorganization is also included
in this commit as well.
Previously `wasm-bindgen` would, in an ad-hoc fashion, run the GC passes
of `walrus` in a bunch of places to ensure that less "garbage" was seen
by future passes. This not only was a source of slowdown but it also was
pretty brittle since `wasm-bindgen` kept breaking if extra iteams leaked
through.
The strategy taken in this commit is to have one precise location for a
GC pass, and everything goes through there. This is achieved by:
* All internal exports are removed immediately when generating the
nonstandard wasm interface types section. Internal exports,
intrinsics, and runtime support are all referenced by the various
instructions and/or sections that use them. This means that we now
have precise tracking of what an adapter uses.
* This in turn enables us to implement the `add_gc_roots` function for
`walrus` custom sections, which in turn allows walrus GC passes to do
what `unexport_unused_intrinsics` did before. That function is now no
longer necessary, but effectively works the same way. All intrinsics
are unexported at the beginning and then they're selectively
re-imported and re-exported through the JS glue generation pass as
necessary and defined by the bindings.
* Passes like the `anyref` pass are now much more precise about the
intrinsics that they work with. The `anyref` pass also deletes any
internal intrinsics found and also does some rewriting of the adapters
aftewards now to hook up calls to the heap count import to the heap
count intrinsic in the wasm module.
* Fix handling of __wbindgen_realloc
The final user of the `require_internal_export` function was
`__wbindgen_realloc`. This usage has now been removed by updating how we
handle usage of the `realloc` function.
The wasm interface types standard doesn't have a `realloc` function
slot, nor do I think it ever will. This means that as a polyfill for
wasm interface types we'll always have to support the lack of `realloc`.
For direct Rust to JS, however, we can still optionally handle
`realloc`. This is all handled with a few internal changes.
* Custom `StringToMemory` instructions now exist. These have an extra
`realloc` slot to store an intrinsic, if found.
* Our custom instructions are lowered to the standard instructions when
generating an interface types section.
* The `realloc` function, if present, is passed as an argument like the
malloc function when passing strings to wasm. If it's not present we
use a slower fallback, but if it's present we use the faster
implementation.
This should mean that there's little-to-no impact on existing users of
`wasm-bindgen`, but this should continue to still work for wasm
interface types polyfills and such. Additionally the GC passes now work
in that they don't delete `__wbindgen_realloc` which we later try to
reference.
* Add an empty test for the anyref pass
* Precisely track I32FromOptionAnyref's dependencies
This depends on the anyref table and a function to allocate an index if
the anyref pass is running, so be sure to track that in the instruction
itself for GC rooting.
* Trim extraneous exports from nop anyref module
Or if you're otherwise not using anyref slices, don't force some
intrinsics to exist.
* Remove globals from reference tests
Looks like these values adjust in slight but insignificant ways over
time
* Update the anyref xform tests
2019-12-04 12:01:39 -06:00
|
|
|
for (_, core, adapter) in nonstandard.implements.iter() {
|
|
|
|
section.implements.add(us2walrus[adapter], *core);
|
2019-12-03 11:16:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
for (name, adapter) in nonstandard.exports.iter() {
|
|
|
|
section.exports.add(name, us2walrus[adapter]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (id, func) in nonstandard.adapters.iter() {
|
|
|
|
let instructions = match &func.kind {
|
|
|
|
AdapterKind::Local { instructions } => instructions,
|
|
|
|
AdapterKind::Import { .. } => continue,
|
|
|
|
};
|
|
|
|
let result = match &mut section.funcs.get_mut(us2walrus[id]).kind {
|
|
|
|
wit_walrus::FuncKind::Local(i) => i,
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
for instruction in instructions {
|
|
|
|
result.push(
|
|
|
|
translate_instruction(instruction, &us2walrus, module)
|
|
|
|
.with_context(|| adapter_context(*id))?,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some((name, _)) = local_modules.iter().next() {
|
|
|
|
bail!(
|
|
|
|
"generating a bindings section is currently incompatible with \
|
|
|
|
local JS modules being specified as well, `{}` cannot be used \
|
|
|
|
since a standalone wasm file is being generated",
|
|
|
|
name,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some((name, _)) = snippets.iter().filter(|(_, v)| !v.is_empty()).next() {
|
|
|
|
bail!(
|
|
|
|
"generating a bindings section is currently incompatible with \
|
|
|
|
local JS snippets being specified as well, `{}` cannot be used \
|
|
|
|
since a standalone wasm file is being generated",
|
|
|
|
name,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(path) = package_jsons.iter().next() {
|
|
|
|
bail!(
|
|
|
|
"generating a bindings section is currently incompatible with \
|
|
|
|
package.json being consumed as well, `{}` cannot be used \
|
|
|
|
since a standalone wasm file is being generated",
|
|
|
|
path.display(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(id) = imports_with_catch.iter().next() {
|
|
|
|
bail!(
|
|
|
|
"{}\ngenerating a bindings section is currently incompatible with \
|
|
|
|
`#[wasm_bindgen(catch)]`",
|
|
|
|
adapter_context(*id),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(id) = imports_with_variadic.iter().next() {
|
|
|
|
bail!(
|
|
|
|
"{}\ngenerating a bindings section is currently incompatible with \
|
|
|
|
`#[wasm_bindgen(variadic)]`",
|
|
|
|
adapter_context(*id),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(enum_) = enums.iter().next() {
|
|
|
|
bail!(
|
|
|
|
"generating a bindings section is currently incompatible with \
|
|
|
|
exporting an `enum` from the wasm file, cannot export `{}`",
|
|
|
|
enum_.name,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(struct_) = structs.iter().next() {
|
|
|
|
bail!(
|
|
|
|
"generating a bindings section is currently incompatible with \
|
|
|
|
exporting a `struct` from the wasm file, cannot export `{}`",
|
|
|
|
struct_.name,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
module.customs.add(section);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn translate_instruction(
|
|
|
|
instr: &InstructionData,
|
|
|
|
us2walrus: &HashMap<AdapterId, wit_walrus::FuncId>,
|
|
|
|
module: &Module,
|
|
|
|
) -> Result<wit_walrus::Instruction, Error> {
|
|
|
|
use Instruction::*;
|
|
|
|
|
|
|
|
match &instr.instr {
|
|
|
|
Standard(s) => Ok(s.clone()),
|
|
|
|
CallAdapter(id) => {
|
|
|
|
let id = us2walrus[id];
|
|
|
|
Ok(wit_walrus::Instruction::CallAdapter(id))
|
|
|
|
}
|
|
|
|
CallExport(e) => match module.exports.get(*e).item {
|
|
|
|
walrus::ExportItem::Function(f) => Ok(wit_walrus::Instruction::CallCore(f)),
|
|
|
|
_ => bail!("can only call exported functions"),
|
|
|
|
},
|
|
|
|
CallTableElement(e) => {
|
|
|
|
let table = module
|
|
|
|
.tables
|
|
|
|
.main_function_table()?
|
|
|
|
.ok_or_else(|| anyhow!("no function table found in module"))?;
|
|
|
|
let functions = match &module.tables.get(table).kind {
|
|
|
|
walrus::TableKind::Function(f) => f,
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
match functions.elements.get(*e as usize) {
|
|
|
|
Some(Some(f)) => Ok(wit_walrus::Instruction::CallCore(*f)),
|
|
|
|
_ => bail!("expected to find an element of the function table"),
|
|
|
|
}
|
|
|
|
}
|
Add reference output tests for JS operations (#1894)
* Add reference output tests for JS operations
This commit starts adding a test suite which checks in, to the
repository, test assertions for both the JS and wasm file outputs of a
Rust crate compiled with `#[wasm_bindgen]`. These aren't intended to be
exhaustive or large scale tests, but rather micro-tests to help observe
the changes in `wasm-bindgen`'s output over time.
The motivation for this commit is basically overhauling how all the GC
passes work in `wasm-bindgen` today. The reorganization is also included
in this commit as well.
Previously `wasm-bindgen` would, in an ad-hoc fashion, run the GC passes
of `walrus` in a bunch of places to ensure that less "garbage" was seen
by future passes. This not only was a source of slowdown but it also was
pretty brittle since `wasm-bindgen` kept breaking if extra iteams leaked
through.
The strategy taken in this commit is to have one precise location for a
GC pass, and everything goes through there. This is achieved by:
* All internal exports are removed immediately when generating the
nonstandard wasm interface types section. Internal exports,
intrinsics, and runtime support are all referenced by the various
instructions and/or sections that use them. This means that we now
have precise tracking of what an adapter uses.
* This in turn enables us to implement the `add_gc_roots` function for
`walrus` custom sections, which in turn allows walrus GC passes to do
what `unexport_unused_intrinsics` did before. That function is now no
longer necessary, but effectively works the same way. All intrinsics
are unexported at the beginning and then they're selectively
re-imported and re-exported through the JS glue generation pass as
necessary and defined by the bindings.
* Passes like the `anyref` pass are now much more precise about the
intrinsics that they work with. The `anyref` pass also deletes any
internal intrinsics found and also does some rewriting of the adapters
aftewards now to hook up calls to the heap count import to the heap
count intrinsic in the wasm module.
* Fix handling of __wbindgen_realloc
The final user of the `require_internal_export` function was
`__wbindgen_realloc`. This usage has now been removed by updating how we
handle usage of the `realloc` function.
The wasm interface types standard doesn't have a `realloc` function
slot, nor do I think it ever will. This means that as a polyfill for
wasm interface types we'll always have to support the lack of `realloc`.
For direct Rust to JS, however, we can still optionally handle
`realloc`. This is all handled with a few internal changes.
* Custom `StringToMemory` instructions now exist. These have an extra
`realloc` slot to store an intrinsic, if found.
* Our custom instructions are lowered to the standard instructions when
generating an interface types section.
* The `realloc` function, if present, is passed as an argument like the
malloc function when passing strings to wasm. If it's not present we
use a slower fallback, but if it's present we use the faster
implementation.
This should mean that there's little-to-no impact on existing users of
`wasm-bindgen`, but this should continue to still work for wasm
interface types polyfills and such. Additionally the GC passes now work
in that they don't delete `__wbindgen_realloc` which we later try to
reference.
* Add an empty test for the anyref pass
* Precisely track I32FromOptionAnyref's dependencies
This depends on the anyref table and a function to allocate an index if
the anyref pass is running, so be sure to track that in the instruction
itself for GC rooting.
* Trim extraneous exports from nop anyref module
Or if you're otherwise not using anyref slices, don't force some
intrinsics to exist.
* Remove globals from reference tests
Looks like these values adjust in slight but insignificant ways over
time
* Update the anyref xform tests
2019-12-04 12:01:39 -06:00
|
|
|
StringToMemory {
|
|
|
|
mem,
|
|
|
|
malloc,
|
|
|
|
realloc: _,
|
|
|
|
} => Ok(wit_walrus::Instruction::StringToMemory {
|
|
|
|
mem: *mem,
|
|
|
|
malloc: *malloc,
|
|
|
|
}),
|
2019-12-03 11:16:44 -06:00
|
|
|
StoreRetptr { .. } | LoadRetptr { .. } | Retptr => {
|
|
|
|
bail!("return pointers aren't supported in wasm interface types");
|
|
|
|
}
|
|
|
|
I32FromBool | BoolFromI32 => {
|
|
|
|
bail!("booleans aren't supported in wasm interface types");
|
|
|
|
}
|
|
|
|
I32FromStringFirstChar | StringFromChar => {
|
|
|
|
bail!("chars aren't supported in wasm interface types");
|
|
|
|
}
|
|
|
|
I32FromAnyrefOwned | I32FromAnyrefBorrow | AnyrefLoadOwned | TableGet => {
|
|
|
|
bail!("anyref pass failed to sink into wasm module");
|
|
|
|
}
|
|
|
|
I32FromAnyrefRustOwned { .. } | I32FromAnyrefRustBorrow { .. } | RustFromI32 { .. } => {
|
|
|
|
bail!("rust types aren't supported in wasm interface types");
|
|
|
|
}
|
|
|
|
I32Split64 { .. } | I64FromLoHi { .. } => {
|
|
|
|
bail!("64-bit integers aren't supported in wasm-bindgen");
|
|
|
|
}
|
|
|
|
I32SplitOption64 { .. }
|
Add reference output tests for JS operations (#1894)
* Add reference output tests for JS operations
This commit starts adding a test suite which checks in, to the
repository, test assertions for both the JS and wasm file outputs of a
Rust crate compiled with `#[wasm_bindgen]`. These aren't intended to be
exhaustive or large scale tests, but rather micro-tests to help observe
the changes in `wasm-bindgen`'s output over time.
The motivation for this commit is basically overhauling how all the GC
passes work in `wasm-bindgen` today. The reorganization is also included
in this commit as well.
Previously `wasm-bindgen` would, in an ad-hoc fashion, run the GC passes
of `walrus` in a bunch of places to ensure that less "garbage" was seen
by future passes. This not only was a source of slowdown but it also was
pretty brittle since `wasm-bindgen` kept breaking if extra iteams leaked
through.
The strategy taken in this commit is to have one precise location for a
GC pass, and everything goes through there. This is achieved by:
* All internal exports are removed immediately when generating the
nonstandard wasm interface types section. Internal exports,
intrinsics, and runtime support are all referenced by the various
instructions and/or sections that use them. This means that we now
have precise tracking of what an adapter uses.
* This in turn enables us to implement the `add_gc_roots` function for
`walrus` custom sections, which in turn allows walrus GC passes to do
what `unexport_unused_intrinsics` did before. That function is now no
longer necessary, but effectively works the same way. All intrinsics
are unexported at the beginning and then they're selectively
re-imported and re-exported through the JS glue generation pass as
necessary and defined by the bindings.
* Passes like the `anyref` pass are now much more precise about the
intrinsics that they work with. The `anyref` pass also deletes any
internal intrinsics found and also does some rewriting of the adapters
aftewards now to hook up calls to the heap count import to the heap
count intrinsic in the wasm module.
* Fix handling of __wbindgen_realloc
The final user of the `require_internal_export` function was
`__wbindgen_realloc`. This usage has now been removed by updating how we
handle usage of the `realloc` function.
The wasm interface types standard doesn't have a `realloc` function
slot, nor do I think it ever will. This means that as a polyfill for
wasm interface types we'll always have to support the lack of `realloc`.
For direct Rust to JS, however, we can still optionally handle
`realloc`. This is all handled with a few internal changes.
* Custom `StringToMemory` instructions now exist. These have an extra
`realloc` slot to store an intrinsic, if found.
* Our custom instructions are lowered to the standard instructions when
generating an interface types section.
* The `realloc` function, if present, is passed as an argument like the
malloc function when passing strings to wasm. If it's not present we
use a slower fallback, but if it's present we use the faster
implementation.
This should mean that there's little-to-no impact on existing users of
`wasm-bindgen`, but this should continue to still work for wasm
interface types polyfills and such. Additionally the GC passes now work
in that they don't delete `__wbindgen_realloc` which we later try to
reference.
* Add an empty test for the anyref pass
* Precisely track I32FromOptionAnyref's dependencies
This depends on the anyref table and a function to allocate an index if
the anyref pass is running, so be sure to track that in the instruction
itself for GC rooting.
* Trim extraneous exports from nop anyref module
Or if you're otherwise not using anyref slices, don't force some
intrinsics to exist.
* Remove globals from reference tests
Looks like these values adjust in slight but insignificant ways over
time
* Update the anyref xform tests
2019-12-04 12:01:39 -06:00
|
|
|
| I32FromOptionAnyref { .. }
|
2019-12-03 11:16:44 -06:00
|
|
|
| I32FromOptionU32Sentinel
|
|
|
|
| I32FromOptionRust { .. }
|
|
|
|
| I32FromOptionBool
|
|
|
|
| I32FromOptionChar
|
|
|
|
| I32FromOptionEnum { .. }
|
|
|
|
| FromOptionNative { .. }
|
|
|
|
| OptionVector { .. }
|
Add reference output tests for JS operations (#1894)
* Add reference output tests for JS operations
This commit starts adding a test suite which checks in, to the
repository, test assertions for both the JS and wasm file outputs of a
Rust crate compiled with `#[wasm_bindgen]`. These aren't intended to be
exhaustive or large scale tests, but rather micro-tests to help observe
the changes in `wasm-bindgen`'s output over time.
The motivation for this commit is basically overhauling how all the GC
passes work in `wasm-bindgen` today. The reorganization is also included
in this commit as well.
Previously `wasm-bindgen` would, in an ad-hoc fashion, run the GC passes
of `walrus` in a bunch of places to ensure that less "garbage" was seen
by future passes. This not only was a source of slowdown but it also was
pretty brittle since `wasm-bindgen` kept breaking if extra iteams leaked
through.
The strategy taken in this commit is to have one precise location for a
GC pass, and everything goes through there. This is achieved by:
* All internal exports are removed immediately when generating the
nonstandard wasm interface types section. Internal exports,
intrinsics, and runtime support are all referenced by the various
instructions and/or sections that use them. This means that we now
have precise tracking of what an adapter uses.
* This in turn enables us to implement the `add_gc_roots` function for
`walrus` custom sections, which in turn allows walrus GC passes to do
what `unexport_unused_intrinsics` did before. That function is now no
longer necessary, but effectively works the same way. All intrinsics
are unexported at the beginning and then they're selectively
re-imported and re-exported through the JS glue generation pass as
necessary and defined by the bindings.
* Passes like the `anyref` pass are now much more precise about the
intrinsics that they work with. The `anyref` pass also deletes any
internal intrinsics found and also does some rewriting of the adapters
aftewards now to hook up calls to the heap count import to the heap
count intrinsic in the wasm module.
* Fix handling of __wbindgen_realloc
The final user of the `require_internal_export` function was
`__wbindgen_realloc`. This usage has now been removed by updating how we
handle usage of the `realloc` function.
The wasm interface types standard doesn't have a `realloc` function
slot, nor do I think it ever will. This means that as a polyfill for
wasm interface types we'll always have to support the lack of `realloc`.
For direct Rust to JS, however, we can still optionally handle
`realloc`. This is all handled with a few internal changes.
* Custom `StringToMemory` instructions now exist. These have an extra
`realloc` slot to store an intrinsic, if found.
* Our custom instructions are lowered to the standard instructions when
generating an interface types section.
* The `realloc` function, if present, is passed as an argument like the
malloc function when passing strings to wasm. If it's not present we
use a slower fallback, but if it's present we use the faster
implementation.
This should mean that there's little-to-no impact on existing users of
`wasm-bindgen`, but this should continue to still work for wasm
interface types polyfills and such. Additionally the GC passes now work
in that they don't delete `__wbindgen_realloc` which we later try to
reference.
* Add an empty test for the anyref pass
* Precisely track I32FromOptionAnyref's dependencies
This depends on the anyref table and a function to allocate an index if
the anyref pass is running, so be sure to track that in the instruction
itself for GC rooting.
* Trim extraneous exports from nop anyref module
Or if you're otherwise not using anyref slices, don't force some
intrinsics to exist.
* Remove globals from reference tests
Looks like these values adjust in slight but insignificant ways over
time
* Update the anyref xform tests
2019-12-04 12:01:39 -06:00
|
|
|
| OptionString { .. }
|
2019-12-03 11:16:44 -06:00
|
|
|
| OptionRustFromI32 { .. }
|
|
|
|
| OptionVectorLoad { .. }
|
|
|
|
| OptionView { .. }
|
|
|
|
| OptionU32Sentinel
|
|
|
|
| ToOptionNative { .. }
|
|
|
|
| OptionBoolFromI32
|
|
|
|
| OptionCharFromI32
|
|
|
|
| OptionEnumFromI32 { .. }
|
|
|
|
| Option64FromI32 { .. } => {
|
|
|
|
bail!("optional types aren't supported in wasm bindgen");
|
|
|
|
}
|
|
|
|
MutableSliceToMemory { .. } | VectorToMemory { .. } | VectorLoad { .. } | View { .. } => {
|
|
|
|
bail!("vector slices aren't supported in wasm interface types yet");
|
|
|
|
}
|
|
|
|
CachedStringLoad { .. } => {
|
|
|
|
bail!("cached strings aren't supported in wasm interface types");
|
|
|
|
}
|
|
|
|
StackClosure { .. } => {
|
|
|
|
bail!("closures aren't supported in wasm interface types");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_standard_import(import: &AuxImport) -> Result<(), Error> {
|
|
|
|
let desc_js = |js: &JsImport| {
|
|
|
|
let mut extra = String::new();
|
|
|
|
for field in js.fields.iter() {
|
|
|
|
extra.push_str(".");
|
|
|
|
extra.push_str(field);
|
|
|
|
}
|
|
|
|
match &js.name {
|
|
|
|
JsImportName::Global { name } | JsImportName::VendorPrefixed { name, .. } => {
|
|
|
|
format!("global `{}{}`", name, extra)
|
|
|
|
}
|
|
|
|
JsImportName::Module { module, name } => {
|
|
|
|
format!("`{}{}` from '{}'", name, extra, module)
|
|
|
|
}
|
|
|
|
JsImportName::LocalModule { module, name } => {
|
|
|
|
format!("`{}{}` from local module '{}'", name, extra, module)
|
|
|
|
}
|
|
|
|
JsImportName::InlineJs {
|
|
|
|
unique_crate_identifier,
|
|
|
|
name,
|
|
|
|
..
|
|
|
|
} => format!(
|
|
|
|
"`{}{}` from inline js in '{}'",
|
|
|
|
name, extra, unique_crate_identifier
|
|
|
|
),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let item = match import {
|
|
|
|
AuxImport::Value(AuxValue::Bare(js)) => {
|
|
|
|
if js.fields.len() == 0 {
|
|
|
|
if let JsImportName::Module { .. } = js.name {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
desc_js(js)
|
|
|
|
}
|
|
|
|
AuxImport::Value(AuxValue::Getter(js, name))
|
|
|
|
| AuxImport::Value(AuxValue::Setter(js, name))
|
|
|
|
| AuxImport::Value(AuxValue::ClassGetter(js, name))
|
|
|
|
| AuxImport::Value(AuxValue::ClassSetter(js, name)) => {
|
|
|
|
format!("field access of `{}` for {}", name, desc_js(js))
|
|
|
|
}
|
|
|
|
AuxImport::ValueWithThis(js, method) => format!("method `{}.{}`", desc_js(js), method),
|
|
|
|
AuxImport::Instanceof(js) => format!("instance of check of {}", desc_js(js)),
|
|
|
|
AuxImport::Static(js) => format!("static js value {}", desc_js(js)),
|
|
|
|
AuxImport::StructuralMethod(name) => format!("structural method `{}`", name),
|
|
|
|
AuxImport::StructuralGetter(name)
|
|
|
|
| AuxImport::StructuralSetter(name)
|
|
|
|
| AuxImport::StructuralClassGetter(_, name)
|
|
|
|
| AuxImport::StructuralClassSetter(_, name) => {
|
|
|
|
format!("structural field access of `{}`", name)
|
|
|
|
}
|
|
|
|
AuxImport::IndexingDeleterOfClass(_)
|
|
|
|
| AuxImport::IndexingDeleterOfObject
|
|
|
|
| AuxImport::IndexingGetterOfClass(_)
|
|
|
|
| AuxImport::IndexingGetterOfObject
|
|
|
|
| AuxImport::IndexingSetterOfClass(_)
|
|
|
|
| AuxImport::IndexingSetterOfObject => format!("indexing getters/setters/deleters"),
|
|
|
|
AuxImport::WrapInExportedClass(name) => {
|
|
|
|
format!("wrapping a pointer in a `{}` js class wrapper", name)
|
|
|
|
}
|
|
|
|
AuxImport::Intrinsic(intrinsic) => {
|
|
|
|
format!("wasm-bindgen specific intrinsic `{}`", intrinsic.name())
|
|
|
|
}
|
|
|
|
AuxImport::Closure { .. } => format!("creating a `Closure` wrapper"),
|
|
|
|
};
|
Add tests for the interface types output of wasm-bindgen (#1898)
* Add tests for the interface types output of wasm-bindgen
This commit expands the test suite with assertions about the output of
the interface types pass in wasm-bindgen. The goal here is to actually
assert that we produce the right output and have a suite of reference
files to show how the interface types output is changing over time.
The `reference` test suite added in the previous PR has been updated to
work for interface types as well, generating `*.wit` file assertions
which are printed via the `wit-printer` crate on crates.io.
Along the way a number of bugs were fixed with the interface types
output, such as:
* Non-determinism in output caused by iteration of a `HashMap`
* Avoiding JS generation entirely in interface types mode, ensuring that
we don't export extraneous intrinsics that aren't otherwise needed.
* Fixing location of the stack pointer for modules where it's GC'd out.
It's now rooted in the aux section of wasm-bindgen so it's available
to later passes, like the multi-value pass.
* Interface types emission now works in debug mode, meaning the
`--release` flag is no longer required. This previously did not work
because the `__wbindgen_throw` intrinsic was required in debug mode.
This comes about because of the `malloc_failure` and `internal_error`
functions in the anyref pass. The purpose of these functions is to
signal fatal runtime errors, if any, in a way that's usable to the
user. For wasm interface types though we can replace calls to these
functions with `unreachable` to avoid needing to import the
intrinsic. This has the accidental side effect of making
`wasm_bindgen::throw_str` "just work" with wasm interface types by
aborting the program, but that's not actually entirely intended. It's
hoped that a split of a `wasm-bindgen-core` crate would solve this
issue for the future.
* Run the wasm interface types validator in tests
* Add more gc roots for adapter gc
* Improve stack pointer detection
The stack pointer is never initialized to zero, but some other mutable
globals are (TLS, thread ID, etc), so let's filter those out.
2019-12-04 15:19:48 -06:00
|
|
|
bail!("import of {} requires JS glue", item);
|
2019-12-03 11:16:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
fn check_standard_export(export: &AuxExport) -> Result<(), Error> {
|
|
|
|
// First up make sure this is something that's actually valid to export
|
|
|
|
// form a vanilla WebAssembly module with WebIDL bindings.
|
|
|
|
match &export.kind {
|
|
|
|
AuxExportKind::Function(_) => Ok(()),
|
|
|
|
AuxExportKind::Constructor(name) => {
|
|
|
|
bail!(
|
|
|
|
"cannot export `{}` constructor function when generating \
|
|
|
|
a standalone WebAssembly module with no JS glue",
|
|
|
|
name,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
AuxExportKind::Getter { class, field } => {
|
|
|
|
bail!(
|
|
|
|
"cannot export `{}::{}` getter function when generating \
|
|
|
|
a standalone WebAssembly module with no JS glue",
|
|
|
|
class,
|
|
|
|
field,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
AuxExportKind::Setter { class, field } => {
|
|
|
|
bail!(
|
|
|
|
"cannot export `{}::{}` setter function when generating \
|
|
|
|
a standalone WebAssembly module with no JS glue",
|
|
|
|
class,
|
|
|
|
field,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
AuxExportKind::StaticFunction { class, name } => {
|
|
|
|
bail!(
|
|
|
|
"cannot export `{}::{}` static function when \
|
|
|
|
generating a standalone WebAssembly module with no \
|
|
|
|
JS glue",
|
|
|
|
class,
|
|
|
|
name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
AuxExportKind::Method { class, name, .. } => {
|
|
|
|
bail!(
|
|
|
|
"cannot export `{}::{}` method when \
|
|
|
|
generating a standalone WebAssembly module with no \
|
|
|
|
JS glue",
|
|
|
|
class,
|
|
|
|
name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn translate_tys(tys: &[AdapterType]) -> Result<Vec<wit_walrus::ValType>, Error> {
|
|
|
|
tys.iter()
|
|
|
|
.map(|ty| {
|
|
|
|
ty.to_wit()
|
|
|
|
.ok_or_else(|| anyhow!("type {:?} isn't supported in standard interface types", ty))
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|