Skip to content

Commit a48a0ae

Browse files
authored
Merge pull request #1654 from fitzgen/no-import-shims
Skip generating JS import shims when unnecessary
2 parents 13b672a + 31ca527 commit a48a0ae

File tree

12 files changed

+315
-18
lines changed

12 files changed

+315
-18
lines changed

crates/backend/src/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ pub struct ImportFunction {
122122
pub catch: bool,
123123
pub variadic: bool,
124124
pub structural: bool,
125+
pub assert_no_shim: bool,
125126
pub kind: ImportFunctionKind,
126127
pub shim: Ident,
127128
pub doc_comment: Option<String>,

crates/backend/src/encode.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ fn shared_import_function<'a>(
272272
shim: intern.intern(&i.shim),
273273
catch: i.catch,
274274
method,
275+
assert_no_shim: i.assert_no_shim,
275276
structural: i.structural,
276277
function: shared_function(&i.function, intern),
277278
variadic: i.variadic,

crates/cli-support/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ wasm-bindgen-anyref-xform = { path = '../anyref-xform', version = '=0.2.48' }
2323
wasm-bindgen-shared = { path = "../shared", version = '=0.2.48' }
2424
wasm-bindgen-threads-xform = { path = '../threads-xform', version = '=0.2.48' }
2525
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.48' }
26-
wasm-webidl-bindings = "0.1.0"
26+
wasm-webidl-bindings = "0.1.2"

crates/cli-support/src/js/mod.rs

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::descriptor::VectorKind;
22
use crate::intrinsic::Intrinsic;
3+
use crate::webidl;
34
use crate::webidl::{AuxEnum, AuxExport, AuxExportKind, AuxImport, AuxStruct};
45
use crate::webidl::{AuxValue, Binding};
56
use crate::webidl::{JsImport, JsImportName, NonstandardWebidlSection, WasmBindgenAux};
@@ -284,7 +285,10 @@ impl<'a> Context<'a> {
284285
| OutputMode::Node {
285286
experimental_modules: true,
286287
} => {
287-
imports.push_str(&format!("import * as wasm from './{}_bg.wasm';\n", module_name));
288+
imports.push_str(&format!(
289+
"import * as wasm from './{}_bg.wasm';\n",
290+
module_name
291+
));
288292
for (id, js) in sorted_iter(&self.wasm_import_definitions) {
289293
let import = self.module.imports.get_mut(*id);
290294
import.module = format!("./{}.js", module_name);
@@ -723,6 +727,15 @@ impl<'a> Context<'a> {
723727
self.global("function getObject(idx) { return heap[idx]; }");
724728
}
725729

730+
fn expose_not_defined(&mut self) {
731+
if !self.should_write_global("not_defined") {
732+
return;
733+
}
734+
self.global(
735+
"function notDefined(what) { return () => { throw new Error(`${what} is not defined`); }; }"
736+
);
737+
}
738+
726739
fn expose_assert_num(&mut self) {
727740
if !self.should_write_global("assert_num") {
728741
return;
@@ -1826,7 +1839,8 @@ impl<'a> Context<'a> {
18261839
for (id, import) in sorted_iter(&aux.import_map) {
18271840
let variadic = aux.imports_with_variadic.contains(&id);
18281841
let catch = aux.imports_with_catch.contains(&id);
1829-
self.generate_import(*id, import, bindings, variadic, catch)
1842+
let assert_no_shim = aux.imports_with_assert_no_shim.contains(&id);
1843+
self.generate_import(*id, import, bindings, variadic, catch, assert_no_shim)
18301844
.with_context(|_| {
18311845
format!("failed to generate bindings for import `{:?}`", import,)
18321846
})?;
@@ -1917,7 +1931,7 @@ impl<'a> Context<'a> {
19171931
let js_doc = builder.js_doc_comments();
19181932
let docs = format_doc_comments(&export.comments, Some(js_doc));
19191933

1920-
// Once we've got all the JS then put it in the right location dependin
1934+
// Once we've got all the JS then put it in the right location depending
19211935
// on what's being exported.
19221936
match &export.kind {
19231937
AuxExportKind::Function(name) => {
@@ -1965,22 +1979,77 @@ impl<'a> Context<'a> {
19651979
bindings: &NonstandardWebidlSection,
19661980
variadic: bool,
19671981
catch: bool,
1982+
assert_no_shim: bool,
19681983
) -> Result<(), Error> {
19691984
let binding = &bindings.imports[&id];
19701985
let webidl = bindings
19711986
.types
19721987
.get::<ast::WebidlFunction>(binding.webidl_ty)
19731988
.unwrap();
1974-
let mut builder = binding::Builder::new(self);
1975-
builder.catch(catch)?;
1976-
let js = builder.process(&binding, &webidl, false, &None, &mut |cx, prelude, args| {
1977-
cx.invoke_import(&binding, import, bindings, args, variadic, prelude)
1978-
})?;
1979-
let js = format!("function{}", js);
1989+
let js = match import {
1990+
AuxImport::Value(AuxValue::Bare(js))
1991+
if !variadic && !catch && self.import_does_not_require_glue(binding, webidl) =>
1992+
{
1993+
self.expose_not_defined();
1994+
let name = self.import_name(js)?;
1995+
format!(
1996+
"typeof {name} == 'function' ? {name} : notDefined('{name}')",
1997+
name = name,
1998+
)
1999+
}
2000+
_ => {
2001+
if assert_no_shim {
2002+
panic!(
2003+
"imported function was annotated with `#[wasm_bindgen(assert_no_shim)]` \
2004+
but we need to generate a JS shim for it:\n\n\
2005+
\timport = {:?}\n\n\
2006+
\tbinding = {:?}\n\n\
2007+
\twebidl = {:?}",
2008+
import, binding, webidl,
2009+
);
2010+
}
2011+
2012+
let mut builder = binding::Builder::new(self);
2013+
builder.catch(catch)?;
2014+
let js = builder.process(
2015+
&binding,
2016+
&webidl,
2017+
false,
2018+
&None,
2019+
&mut |cx, prelude, args| {
2020+
cx.invoke_import(&binding, import, bindings, args, variadic, prelude)
2021+
},
2022+
)?;
2023+
format!("function{}", js)
2024+
}
2025+
};
19802026
self.wasm_import_definitions.insert(id, js);
19812027
Ok(())
19822028
}
19832029

2030+
fn import_does_not_require_glue(
2031+
&self,
2032+
binding: &Binding,
2033+
webidl: &ast::WebidlFunction,
2034+
) -> bool {
2035+
if !self.config.anyref && binding.contains_anyref(self.module) {
2036+
return false;
2037+
}
2038+
2039+
let wasm_ty = self.module.types.get(binding.wasm_ty);
2040+
webidl.kind == ast::WebidlFunctionKind::Static
2041+
&& webidl::outgoing_do_not_require_glue(
2042+
&binding.outgoing,
2043+
wasm_ty.params(),
2044+
&webidl.params,
2045+
)
2046+
&& webidl::incoming_do_not_require_glue(
2047+
&binding.incoming,
2048+
&webidl.result.into_iter().collect::<Vec<_>>(),
2049+
wasm_ty.results(),
2050+
)
2051+
}
2052+
19842053
/// Generates a JS snippet appropriate for invoking `import`.
19852054
///
19862055
/// This is generating code for `binding` where `bindings` has more type
@@ -2060,7 +2129,7 @@ impl<'a> Context<'a> {
20602129
ast::WebidlFunctionKind::Static => {
20612130
let js = match val {
20622131
AuxValue::Bare(js) => self.import_name(js)?,
2063-
_ => bail!("invalid import set for constructor"),
2132+
_ => bail!("invalid import set for free function"),
20642133
};
20652134
Ok(format!("{}({})", js, variadic_args(&args)?))
20662135
}

crates/cli-support/src/webidl/mod.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,17 @@ pub struct Binding {
120120
pub return_via_outptr: Option<Vec<walrus::ValType>>,
121121
}
122122

123+
impl Binding {
124+
/// Does this binding's wasm function signature have any `anyref`s?
125+
pub fn contains_anyref(&self, module: &walrus::Module) -> bool {
126+
let ty = module.types.get(self.wasm_ty);
127+
ty.params()
128+
.iter()
129+
.chain(ty.results())
130+
.any(|ty| *ty == walrus::ValType::Anyref)
131+
}
132+
}
133+
123134
/// A synthetic custom section which is not standardized, never will be, and
124135
/// cannot be serialized or parsed. This is synthesized from all of the
125136
/// compiler-emitted wasm-bindgen sections and then immediately removed to be
@@ -152,6 +163,7 @@ pub struct WasmBindgenAux {
152163
/// Small bits of metadata about imports.
153164
pub imports_with_catch: HashSet<ImportId>,
154165
pub imports_with_variadic: HashSet<ImportId>,
166+
pub imports_with_assert_no_shim: HashSet<ImportId>,
155167

156168
/// Auxiliary information to go into JS/TypeScript bindings describing the
157169
/// exported enums from Rust.
@@ -782,6 +794,7 @@ impl<'a> Context<'a> {
782794
method,
783795
structural,
784796
function,
797+
assert_no_shim,
785798
} = function;
786799
let (import_id, _id) = match self.function_imports.get(*shim) {
787800
Some(pair) => *pair,
@@ -800,6 +813,9 @@ impl<'a> Context<'a> {
800813
if *catch {
801814
self.aux.imports_with_catch.insert(import_id);
802815
}
816+
if *assert_no_shim {
817+
self.aux.imports_with_assert_no_shim.insert(import_id);
818+
}
803819

804820
// Perform two functions here. First we're saving off our WebIDL
805821
// bindings signature, indicating what we think our import is going to
@@ -1428,3 +1444,49 @@ fn concatenate_comments(comments: &[&str]) -> String {
14281444
.collect::<Vec<_>>()
14291445
.join("\n")
14301446
}
1447+
1448+
/// Do we need to generate JS glue shims for these incoming bindings?
1449+
pub fn incoming_do_not_require_glue(
1450+
exprs: &[NonstandardIncoming],
1451+
from_webidl_tys: &[ast::WebidlTypeRef],
1452+
to_wasm_tys: &[walrus::ValType],
1453+
) -> bool {
1454+
exprs.len() == from_webidl_tys.len()
1455+
&& exprs.len() == to_wasm_tys.len()
1456+
&& exprs
1457+
.iter()
1458+
.zip(from_webidl_tys)
1459+
.zip(to_wasm_tys)
1460+
.enumerate()
1461+
.all(|(i, ((expr, from_webidl_ty), to_wasm_ty))| match expr {
1462+
NonstandardIncoming::Standard(e) => e.is_expressible_in_js_without_webidl_bindings(
1463+
*from_webidl_ty,
1464+
*to_wasm_ty,
1465+
i as u32,
1466+
),
1467+
_ => false,
1468+
})
1469+
}
1470+
1471+
/// Do we need to generate JS glue shims for these outgoing bindings?
1472+
pub fn outgoing_do_not_require_glue(
1473+
exprs: &[NonstandardOutgoing],
1474+
from_wasm_tys: &[walrus::ValType],
1475+
to_webidl_tys: &[ast::WebidlTypeRef],
1476+
) -> bool {
1477+
exprs.len() == from_wasm_tys.len()
1478+
&& exprs.len() == to_webidl_tys.len()
1479+
&& exprs
1480+
.iter()
1481+
.zip(from_wasm_tys)
1482+
.zip(to_webidl_tys)
1483+
.enumerate()
1484+
.all(|(i, ((expr, from_wasm_ty), to_webidl_ty))| match expr {
1485+
NonstandardOutgoing::Standard(e) => e.is_expressible_in_js_without_webidl_bindings(
1486+
*from_wasm_ty,
1487+
*to_webidl_ty,
1488+
i as u32,
1489+
),
1490+
_ => false,
1491+
})
1492+
}

crates/cli-support/src/webidl/outgoing.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,12 @@ impl OutgoingBuilder<'_> {
200200
Descriptor::U16 => self.standard_as(ValType::I32, ast::WebidlScalarType::UnsignedShort),
201201
Descriptor::I32 => self.standard_as(ValType::I32, ast::WebidlScalarType::Long),
202202
Descriptor::U32 => self.standard_as(ValType::I32, ast::WebidlScalarType::UnsignedLong),
203-
Descriptor::F32 => self.standard_as(ValType::F32, ast::WebidlScalarType::Float),
204-
Descriptor::F64 => self.standard_as(ValType::F64, ast::WebidlScalarType::Double),
203+
Descriptor::F32 => {
204+
self.standard_as(ValType::F32, ast::WebidlScalarType::UnrestrictedFloat)
205+
}
206+
Descriptor::F64 => {
207+
self.standard_as(ValType::F64, ast::WebidlScalarType::UnrestrictedDouble)
208+
}
205209
Descriptor::Enum { .. } => self.standard_as(ValType::I32, ast::WebidlScalarType::Long),
206210

207211
Descriptor::Char => {

crates/macro-support/src/parser.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ macro_rules! attrgen {
5252
(typescript_custom_section, TypescriptCustomSection(Span)),
5353
(start, Start(Span)),
5454
(skip, Skip(Span)),
55+
56+
// For testing purposes only.
57+
(assert_no_shim, AssertNoShim(Span)),
5558
}
5659
};
5760
}
@@ -495,8 +498,10 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a ast::ImportModule)> for syn::ForeignIte
495498
return Err(Diagnostic::span_error(*span, msg));
496499
}
497500
}
501+
let assert_no_shim = opts.assert_no_shim().is_some();
498502
let ret = ast::ImportKind::Function(ast::ImportFunction {
499503
function: wasm,
504+
assert_no_shim,
500505
kind,
501506
js_ret,
502507
catch,

crates/shared/src/lib.rs

100644100755
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ macro_rules! shared_api {
4444
shim: &'a str,
4545
catch: bool,
4646
variadic: bool,
47+
assert_no_shim: bool,
4748
method: Option<MethodData<'a>>,
4849
structural: bool,
4950
function: Function<'a>,

crates/webidl/src/util.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ impl<'src> FirstPassRecord<'src> {
314314
variadic,
315315
catch,
316316
structural,
317+
assert_no_shim: false,
317318
shim: {
318319
let ns = match kind {
319320
ast::ImportFunctionKind::Normal => "",

tests/wasm/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub mod imports;
2929
pub mod js_objects;
3030
pub mod jscast;
3131
pub mod math;
32+
pub mod no_shims;
3233
pub mod node;
3334
pub mod option;
3435
pub mod optional_primitives;

0 commit comments

Comments
 (0)