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

@ -68,15 +68,6 @@ pub struct Js2Rust<'a, 'b: 'a> {
/// The string value here is the class that this should be a constructor
/// for.
constructor: Option<String>,
/// metadata for anyref transformations
anyref_args: Vec<(usize, bool)>,
ret_anyref: bool,
}
pub enum ExportedShim<'a> {
Named(&'a str),
TableElement(&'a mut u32),
}
impl<'a, 'b> Js2Rust<'a, 'b> {
@ -92,8 +83,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
ret_ty: String::new(),
ret_expr: String::new(),
constructor: None,
anyref_args: Vec::new(),
ret_anyref: false,
}
}
@ -104,17 +93,26 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
function: &Function,
opt_arg_names: &Option<Vec<String>>,
) -> Result<&mut Self, Error> {
if let Some(arg_names) = opt_arg_names {
assert_eq!(arg_names.len(), function.arguments.len());
for (arg, arg_name) in function.arguments.iter().zip(arg_names) {
self.argument(arg, arg_name.as_str())?;
}
} else {
for arg in function.arguments.iter() {
self.argument(arg, None)?;
}
let arg_names = match opt_arg_names {
Some(arg_names) => arg_names.iter().map(|s| Some(s.as_str())).collect(),
None => vec![None; function.arguments.len()],
};
assert_eq!(arg_names.len(), function.arguments.len());
for (arg, arg_name) in function.arguments.iter().zip(arg_names) {
// Process the function argument and assert that the metadata about
// the number of arguments on the Rust side required is correct.
let before = self.rust_arguments.len();
self.argument(arg, arg_name)?;
arg.assert_abi_arg_correct(before, self.rust_arguments.len());
}
// Process the return argument, and assert that the metadata returned
// about the descriptor is indeed correct.
let before = self.rust_arguments.len();
self.ret(&function.ret)?;
function
.ret
.assert_abi_return_correct(before, self.rust_arguments.len());
Ok(self)
}
@ -181,12 +179,9 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
ret
}
pub fn argument<'c, I>(&mut self, arg: &Descriptor, opt_arg_name: I) -> Result<&mut Self, Error>
where
I: Into<Option<&'c str>>,
{
fn argument(&mut self, arg: &Descriptor, arg_name: Option<&str>) -> Result<&mut Self, Error> {
let i = self.arg_idx;
let name = self.abi_arg(opt_arg_name.into());
let name = self.abi_arg(arg_name);
let (arg, optional) = match arg {
Descriptor::Option(t) => (&**t, true),
@ -252,7 +247,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.rust_arguments
.push(format!("isLikeNone({0}) ? 0 : addToAnyrefTable({0})", name));
} else {
self.anyref_args.push((self.rust_arguments.len(), true));
self.rust_arguments.push(name);
}
} else {
@ -449,7 +443,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.js_arguments
.push(JsArgument::required(name.clone(), "any".to_string()));
if self.cx.config.anyref {
self.anyref_args.push((self.rust_arguments.len(), false));
self.rust_arguments.push(name);
} else {
// the "stack-ful" nature means that we're always popping from the
@ -492,7 +485,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
Ok(self)
}
pub fn ret(&mut self, ty: &Descriptor) -> Result<&mut Self, Error> {
fn ret(&mut self, ty: &Descriptor) -> Result<&mut Self, Error> {
if let Some(name) = ty.rust_struct() {
match &self.constructor {
Some(class) if class == name => {
@ -566,7 +559,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
if ty.is_anyref() {
self.ret_ty = "any".to_string();
self.ret_expr = format!("return {};", self.cx.take_object("RET"));
self.ret_anyref = true;
return Ok(self);
}
@ -784,12 +776,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
/// Returns two strings, the first of which is the JS expression for the
/// generated function shim and the second is a TypeScript signature of the
/// JS expression.
pub fn finish(
&mut self,
prefix: &str,
invoc: &str,
exported_shim: ExportedShim,
) -> (String, String, String) {
pub fn finish(&mut self, prefix: &str, invoc: &str) -> (String, String, String) {
let js_args = self
.js_arguments
.iter()
@ -854,23 +841,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
}
ts.push(';');
if self.ret_anyref || self.anyref_args.len() > 0 {
match exported_shim {
ExportedShim::Named(name) => {
self.cx
.anyref
.export_xform(name, &self.anyref_args, self.ret_anyref);
}
ExportedShim::TableElement(idx) => {
*idx = self.cx.anyref.table_element_xform(
*idx,
&self.anyref_args,
self.ret_anyref,
);
}
}
}
(js, ts, self.js_doc_comments())
}