mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-16 06:21:22 +00:00
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:
@ -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())
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ mod js2rust;
|
||||
mod rust2js;
|
||||
|
||||
use crate::descriptor::VectorKind;
|
||||
use crate::js::js2rust::{ExportedShim, Js2Rust};
|
||||
use crate::js::js2rust::Js2Rust;
|
||||
use crate::js::rust2js::Rust2Js;
|
||||
use crate::webidl::{AuxEnum, AuxExport, AuxExportKind, AuxImport, AuxStruct};
|
||||
use crate::webidl::{JsImport, JsImportName, WasmBindgenAux, WebidlCustomSection};
|
||||
@ -47,8 +47,6 @@ pub struct Context<'a> {
|
||||
/// A map of the name of npm dependencies we've loaded so far to the path
|
||||
/// they're defined in as well as their version specification.
|
||||
pub npm_dependencies: HashMap<String, (PathBuf, String)>,
|
||||
|
||||
pub anyref: wasm_bindgen_anyref_xform::Context,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -99,7 +97,6 @@ impl<'a> Context<'a> {
|
||||
module,
|
||||
function_table_needed: false,
|
||||
memory,
|
||||
anyref: Default::default(),
|
||||
npm_dependencies: Default::default(),
|
||||
})
|
||||
}
|
||||
@ -192,13 +189,6 @@ impl<'a> Context<'a> {
|
||||
// `__wrap` and such.
|
||||
self.write_classes()?;
|
||||
|
||||
// And now that we're almost ready, run the final "anyref" pass. This is
|
||||
// where we transform a wasm module which doesn't actually use `anyref`
|
||||
// anywhere to using the type internally. The transformation here is
|
||||
// based off all the previous data we've collected so far for each
|
||||
// import/export listed.
|
||||
// self.anyref.run(self.module)?;
|
||||
|
||||
// We're almost done here, so we can delete any internal exports (like
|
||||
// `__wbindgen_malloc`) if none of our JS glue actually needed it.
|
||||
self.unexport_unused_internal_exports();
|
||||
@ -1794,9 +1784,17 @@ impl<'a> Context<'a> {
|
||||
if !self.should_write_global("anyref_table") {
|
||||
return;
|
||||
}
|
||||
self.module
|
||||
.exports
|
||||
.add("__wbg_anyref_table", self.anyref.anyref_table_id());
|
||||
let table = self
|
||||
.module
|
||||
.tables
|
||||
.iter()
|
||||
.find(|t| match t.kind {
|
||||
walrus::TableKind::Anyref(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.expect("failed to find anyref table in module")
|
||||
.id();
|
||||
self.module.exports.add("__wbg_anyref_table", table);
|
||||
}
|
||||
|
||||
fn expose_add_to_anyref_table(&mut self) -> Result<(), Error> {
|
||||
@ -1878,11 +1876,7 @@ impl<'a> Context<'a> {
|
||||
AuxExportKind::Function(name) => {
|
||||
let (js, ts, js_doc) = Js2Rust::new(&name, self)
|
||||
.process(&descriptor, &export.arg_names)?
|
||||
.finish(
|
||||
"function",
|
||||
&format!("wasm.{}", wasm_name),
|
||||
ExportedShim::Named(&wasm_name),
|
||||
);
|
||||
.finish("function", &format!("wasm.{}", wasm_name));
|
||||
self.export(
|
||||
&name,
|
||||
&js,
|
||||
@ -1897,11 +1891,7 @@ impl<'a> Context<'a> {
|
||||
let (js, ts, raw_docs) = Js2Rust::new("constructor", self)
|
||||
.constructor(Some(&class))
|
||||
.process(&descriptor, &export.arg_names)?
|
||||
.finish(
|
||||
"",
|
||||
&format!("wasm.{}", wasm_name),
|
||||
ExportedShim::Named(&wasm_name),
|
||||
);
|
||||
.finish("", &format!("wasm.{}", wasm_name));
|
||||
let exported = require_class(&mut self.exported_classes, class);
|
||||
if exported.has_constructor {
|
||||
bail!("found duplicate constructor for class `{}`", class);
|
||||
@ -1924,11 +1914,9 @@ impl<'a> Context<'a> {
|
||||
j2r.method(false);
|
||||
}
|
||||
}
|
||||
let (js, ts, raw_docs) = j2r.process(&descriptor, &export.arg_names)?.finish(
|
||||
"",
|
||||
&format!("wasm.{}", wasm_name),
|
||||
ExportedShim::Named(&wasm_name),
|
||||
);
|
||||
let (js, ts, raw_docs) = j2r
|
||||
.process(&descriptor, &export.arg_names)?
|
||||
.finish("", &format!("wasm.{}", wasm_name));
|
||||
let ret_ty = j2r.ret_ty.clone();
|
||||
let exported = require_class(&mut self.exported_classes, class);
|
||||
let docs = format_doc_comments(&export.comments, Some(raw_docs));
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::intrinsic::Intrinsic;
|
||||
use crate::js::js2rust::ExportedShim;
|
||||
use crate::js::{Context, Js2Rust};
|
||||
use crate::webidl::{AuxImport, AuxValue, ImportBinding};
|
||||
use failure::{bail, Error};
|
||||
@ -119,9 +118,19 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
}
|
||||
};
|
||||
for arg in function.arguments.iter() {
|
||||
// Process the function argument and assert that the metadata about
|
||||
// the number of arguments on the Rust side required is correct.
|
||||
let before = self.shim_arguments.len();
|
||||
self.argument(arg)?;
|
||||
arg.assert_abi_arg_correct(before, self.shim_arguments.len());
|
||||
}
|
||||
// Process the return argument, and assert that the metadata returned
|
||||
// about the descriptor is indeed correct.
|
||||
let before = self.shim_arguments.len();
|
||||
self.ret(&function.ret)?;
|
||||
function
|
||||
.ret
|
||||
.assert_abi_return_correct(before, self.shim_arguments.len());
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
@ -308,7 +317,6 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
|
||||
if let Some((f, mutable)) = arg.stack_closure() {
|
||||
let arg2 = self.shim_argument();
|
||||
let mut shim = f.shim_idx;
|
||||
let (js, _ts, _js_doc) = {
|
||||
let mut builder = Js2Rust::new("", self.cx);
|
||||
if mutable {
|
||||
@ -320,11 +328,10 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
} else {
|
||||
builder.rust_argument("this.a");
|
||||
}
|
||||
builder.rust_argument("this.b").process(f, &None)?.finish(
|
||||
"function",
|
||||
"this.f",
|
||||
ExportedShim::TableElement(&mut shim),
|
||||
)
|
||||
builder
|
||||
.rust_argument("this.b")
|
||||
.process(f, &None)?
|
||||
.finish("function", "this.f")
|
||||
};
|
||||
self.cx.function_table_needed = true;
|
||||
self.global_idx();
|
||||
@ -338,7 +345,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
abi,
|
||||
arg2,
|
||||
js = js,
|
||||
idx = shim,
|
||||
idx = f.shim_idx,
|
||||
));
|
||||
self.finally(&format!("cb{0}.a = cb{0}.b = 0;", abi));
|
||||
self.js_arguments.push(format!("cb{0}.bind(cb{0})", abi));
|
||||
@ -676,7 +683,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
format!("new {}({})", js, variadic_args(&self.js_arguments)?)
|
||||
}
|
||||
Style::Method => {
|
||||
let descriptor = |anchor: &str, extra: &str, field: &str, which:&str| {
|
||||
let descriptor = |anchor: &str, extra: &str, field: &str, which: &str| {
|
||||
format!(
|
||||
"GetOwnOrInheritedPropertyDescriptor({}{}, '{}').{}",
|
||||
anchor, extra, field, which
|
||||
@ -741,7 +748,6 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
assert!(self.style == Style::Function);
|
||||
assert!(!variadic);
|
||||
assert_eq!(self.js_arguments.len(), 3);
|
||||
let mut shim = closure.shim_idx;
|
||||
let (js, _ts, _js_doc) = {
|
||||
let mut builder = Js2Rust::new("", self.cx);
|
||||
|
||||
@ -777,11 +783,9 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
.finally("this.a = 0;")
|
||||
.finally("}");
|
||||
}
|
||||
builder.process(&closure.function, &None)?.finish(
|
||||
"function",
|
||||
"f",
|
||||
ExportedShim::TableElement(&mut shim),
|
||||
)
|
||||
builder
|
||||
.process(&closure.function, &None)?
|
||||
.finish("function", "f")
|
||||
};
|
||||
self.cx.function_table_needed = true;
|
||||
let body = format!(
|
||||
@ -795,7 +799,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
let real = cb.bind(cb);
|
||||
real.original = cb;
|
||||
",
|
||||
shim,
|
||||
closure.shim_idx,
|
||||
closure.dtor_idx,
|
||||
&self.js_arguments[1],
|
||||
js,
|
||||
@ -978,32 +982,6 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
ret.push_str(&invoc);
|
||||
ret.push_str("\n}\n");
|
||||
|
||||
// if self.ret_anyref || self.anyref_args.len() > 0 {
|
||||
// // Some return values go at the the beginning of the argument list
|
||||
// // (they force a return pointer). Handle that here by offsetting all
|
||||
// // our arg indices by one, but throw in some sanity checks for if
|
||||
// // this ever changes.
|
||||
// if let Some(start) = self.shim_arguments.get(0) {
|
||||
// if start == "ret" {
|
||||
// assert!(!self.ret_anyref);
|
||||
// if let Some(next) = self.shim_arguments.get(1) {
|
||||
// assert_eq!(next, "arg0");
|
||||
// }
|
||||
// for (idx, _) in self.anyref_args.iter_mut() {
|
||||
// *idx += 1;
|
||||
// }
|
||||
// } else {
|
||||
// assert_eq!(start, "arg0");
|
||||
// }
|
||||
// }
|
||||
// self.cx.anyref.import_xform(
|
||||
// "__wbindgen_placeholder__",
|
||||
// shim,
|
||||
// &self.anyref_args,
|
||||
// self.ret_anyref,
|
||||
// );
|
||||
// }
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user