Reimplement anyref processing and passes

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 commit is contained in:
Alex Crichton
2019-05-31 10:54:03 -07:00
parent 55fc5367a5
commit b51df39bc9
7 changed files with 264 additions and 193 deletions

View File

@ -0,0 +1,87 @@
use crate::descriptor::Function;
use crate::webidl::{ImportBinding, WasmBindgenAux, WebidlCustomSection, AuxImport};
use failure::Error;
use std::collections::HashSet;
use walrus::Module;
pub fn process(module: &mut Module) -> Result<(), Error> {
let mut cfg = wasm_bindgen_anyref_xform::Context::default();
cfg.prepare(module)?;
let bindings = module
.customs
.get_typed::<WebidlCustomSection>()
.expect("webidl custom section should exist");
for (export, binding) in bindings.exports.iter() {
let (args, ret) = extract_anyrefs(binding);
cfg.export_xform(*export, &args, ret);
}
for (import, kind) in bindings.imports.iter() {
let binding = match kind {
ImportBinding::Function(f) => f,
ImportBinding::Constructor(f) => f,
ImportBinding::Method(f) => f,
};
let (args, ret) = extract_anyrefs(binding);
cfg.import_xform(*import, &args, ret);
}
let aux = module
.customs
.get_typed_mut::<WasmBindgenAux>()
.expect("webidl custom section should exist");
for import in aux.import_map.values_mut() {
let closure = match import {
AuxImport::Closure(f) => f,
_ => continue,
};
let (args, ret) = extract_anyrefs(&closure.function);
if let Some(new) = cfg.table_element_xform(closure.shim_idx, &args, ret) {
closure.shim_idx = new;
}
}
cfg.run(module)?;
walrus::passes::gc::run(module);
// The GC pass above may end up removing some imported intrinsics. For
// example `__wbindgen_object_clone_ref` is no longer needed after the
// anyref pass. Make sure to delete the associated metadata for those
// intrinsics so we don't try to access stale intrinsics later on.
let remaining_imports = module
.imports
.iter()
.map(|i| i.id())
.collect::<HashSet<_>>();
module
.customs
.get_typed_mut::<WebidlCustomSection>()
.expect("webidl custom section should exist")
.imports
.retain(|id, _| remaining_imports.contains(id));
module
.customs
.get_typed_mut::<WasmBindgenAux>()
.expect("wasm-bindgen aux section should exist")
.import_map
.retain(|id, _| remaining_imports.contains(id));
Ok(())
}
fn extract_anyrefs(f: &Function) -> (Vec<(usize, bool)>, bool) {
let mut args = Vec::new();
let mut cur = 0;
if f.ret.abi_returned_through_pointer() {
cur += 1;
}
for arg in f.arguments.iter() {
if arg.is_anyref() {
args.push((cur, true));
} else if arg.is_ref_anyref() {
args.push((cur, false));
}
cur += arg.abi_arg_count();
}
(args, f.ret.is_anyref())
}