cli-support: Skip generating JS shims for imports when unnecessary

After this change, any import that only takes and returns ABI-safe numbers (signed
integers less than 64 bits and unrestricted floating point numbers) will be a
direct import, and will not have a little JS shim in the middle.

We don't have a great mechanism for testing the generated bindings' contents --
as opposed to its behavior -- but I manually verified that everything here does
the Right Thing and doesn't have a JS shim:

```rust
\#[wasm_bindgen]
extern "C" {
    fn trivial();

    fn incoming_i32() -> i32;
    fn incoming_f32() -> f32;
    fn incoming_f64() -> f64;

    fn outgoing_i32(x: i32);
    fn outgoing_f32(y: f32);
    fn outgoing_f64(z: f64);

    fn many(x: i32, y: f32, z: f64) -> i32;
}
```

Furthermore, I verified that when our support for emitting native `anyref` is
enabled, then we do not have a JS shim for the following import, but if it is
disabled, then we do have a JS shim:

```rust
\#[wasm_bindgen]
extern "C" {
    fn works_when_anyref_support_is_enabled(v: JsValue) -> JsValue;
}
```

Fixes #1636.
This commit is contained in:
Nick Fitzgerald
2019-07-10 14:22:13 -07:00
parent f2a4694c69
commit d5d3e46334
5 changed files with 199 additions and 8 deletions

View File

@ -1,5 +1,6 @@
use crate::descriptor::VectorKind;
use crate::intrinsic::Intrinsic;
use crate::webidl;
use crate::webidl::{AuxEnum, AuxExport, AuxExportKind, AuxImport, AuxStruct};
use crate::webidl::{AuxValue, Binding};
use crate::webidl::{JsImport, JsImportName, NonstandardWebidlSection, WasmBindgenAux};
@ -723,6 +724,15 @@ impl<'a> Context<'a> {
self.global("function getObject(idx) { return heap[idx]; }");
}
fn expose_not_defined(&mut self) {
if !self.should_write_global("not_defined") {
return;
}
self.global(
"function notDefined(what) { return () => { throw new Error(`${what} is not defined`); }; }"
);
}
fn expose_assert_num(&mut self) {
if !self.should_write_global("assert_num") {
return;
@ -1971,16 +1981,59 @@ impl<'a> Context<'a> {
.types
.get::<ast::WebidlFunction>(binding.webidl_ty)
.unwrap();
let mut builder = binding::Builder::new(self);
builder.catch(catch)?;
let js = builder.process(&binding, &webidl, false, &None, &mut |cx, prelude, args| {
cx.invoke_import(&binding, import, bindings, args, variadic, prelude)
})?;
let js = format!("function{}", js);
let js = match import {
AuxImport::Value(AuxValue::Bare(js))
if !variadic && !catch && self.import_does_not_require_glue(binding, webidl) =>
{
self.expose_not_defined();
let name = self.import_name(js)?;
format!(
"typeof {name} == 'function' ? {name} : notDefined('{name}')",
name = name,
)
}
_ => {
let mut builder = binding::Builder::new(self);
builder.catch(catch)?;
let js = builder.process(
&binding,
&webidl,
false,
&None,
&mut |cx, prelude, args| {
cx.invoke_import(&binding, import, bindings, args, variadic, prelude)
},
)?;
format!("function{}", js)
}
};
self.wasm_import_definitions.insert(id, js);
Ok(())
}
fn import_does_not_require_glue(
&self,
binding: &Binding,
webidl: &ast::WebidlFunction,
) -> bool {
if !self.config.anyref && binding.contains_anyref(self.module) {
return false;
}
let wasm_ty = self.module.types.get(binding.wasm_ty);
webidl.kind == ast::WebidlFunctionKind::Static
&& webidl::outgoing_do_not_require_glue(
&binding.outgoing,
wasm_ty.params(),
&webidl.params,
)
&& webidl::incoming_do_not_require_glue(
&binding.incoming,
&webidl.result.into_iter().collect::<Vec<_>>(),
wasm_ty.results(),
)
}
/// Generates a JS snippet appropriate for invoking `import`.
///
/// This is generating code for `binding` where `bindings` has more type

View File

@ -120,6 +120,17 @@ pub struct Binding {
pub return_via_outptr: Option<Vec<walrus::ValType>>,
}
impl Binding {
/// Does this binding's wasm function signature have any `anyref`s?
pub fn contains_anyref(&self, module: &walrus::Module) -> bool {
let ty = module.types.get(self.wasm_ty);
ty.params()
.iter()
.chain(ty.results())
.any(|ty| *ty == walrus::ValType::Anyref)
}
}
/// A synthetic custom section which is not standardized, never will be, and
/// cannot be serialized or parsed. This is synthesized from all of the
/// compiler-emitted wasm-bindgen sections and then immediately removed to be
@ -1428,3 +1439,49 @@ fn concatenate_comments(comments: &[&str]) -> String {
.collect::<Vec<_>>()
.join("\n")
}
/// Do we need to generate JS glue shims for these incoming bindings?
pub fn incoming_do_not_require_glue(
exprs: &[NonstandardIncoming],
from_webidl_tys: &[ast::WebidlTypeRef],
to_wasm_tys: &[walrus::ValType],
) -> bool {
exprs.len() == from_webidl_tys.len()
&& exprs.len() == to_wasm_tys.len()
&& exprs
.iter()
.zip(from_webidl_tys)
.zip(to_wasm_tys)
.enumerate()
.all(|(i, ((expr, from_webidl_ty), to_wasm_ty))| match expr {
NonstandardIncoming::Standard(e) => e.is_expressible_in_js_without_webidl_bindings(
*from_webidl_ty,
*to_wasm_ty,
i as u32,
),
_ => false,
})
}
/// Do we need to generate JS glue shims for these outgoing bindings?
pub fn outgoing_do_not_require_glue(
exprs: &[NonstandardOutgoing],
from_wasm_tys: &[walrus::ValType],
to_webidl_tys: &[ast::WebidlTypeRef],
) -> bool {
exprs.len() == from_wasm_tys.len()
&& exprs.len() == to_webidl_tys.len()
&& exprs
.iter()
.zip(from_wasm_tys)
.zip(to_webidl_tys)
.enumerate()
.all(|(i, ((expr, from_wasm_ty), to_webidl_ty))| match expr {
NonstandardOutgoing::Standard(e) => e.is_expressible_in_js_without_webidl_bindings(
*from_wasm_ty,
*to_webidl_ty,
i as u32,
),
_ => false,
})
}

View File

@ -200,8 +200,12 @@ impl OutgoingBuilder<'_> {
Descriptor::U16 => self.standard_as(ValType::I32, ast::WebidlScalarType::UnsignedShort),
Descriptor::I32 => self.standard_as(ValType::I32, ast::WebidlScalarType::Long),
Descriptor::U32 => self.standard_as(ValType::I32, ast::WebidlScalarType::UnsignedLong),
Descriptor::F32 => self.standard_as(ValType::F32, ast::WebidlScalarType::Float),
Descriptor::F64 => self.standard_as(ValType::F64, ast::WebidlScalarType::Double),
Descriptor::F32 => {
self.standard_as(ValType::F32, ast::WebidlScalarType::UnrestrictedFloat)
}
Descriptor::F64 => {
self.standard_as(ValType::F64, ast::WebidlScalarType::UnrestrictedDouble)
}
Descriptor::Enum { .. } => self.standard_as(ValType::I32, ast::WebidlScalarType::Long),
Descriptor::Char => {