Skip to content

Commit 732b691

Browse files
authored
Merge pull request #1690 from alexcrichton/webidl-polyfill
Add support as a vanilla polyfill of WebIDL bindings
2 parents 117fce1 + 452ce29 commit 732b691

File tree

2 files changed

+171
-1
lines changed

2 files changed

+171
-1
lines changed

crates/cli-support/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ impl Bindgen {
259259
.generate_dwarf(self.keep_debug)
260260
.generate_name_section(!self.remove_name_section)
261261
.generate_producers_section(!self.remove_producers_section)
262+
.on_parse(wasm_webidl_bindings::binary::on_parse)
262263
.parse(&contents)
263264
.context("failed to parse input file as wasm")?;
264265
let stem = match &self.out_name {

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

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::decode;
2727
use crate::descriptor::{Descriptor, Function};
2828
use crate::descriptors::WasmBindgenDescriptorsSection;
2929
use crate::intrinsic::Intrinsic;
30-
use failure::{bail, Error};
30+
use failure::{bail, format_err, Error};
3131
use std::borrow::Cow;
3232
use std::collections::{HashMap, HashSet};
3333
use std::path::PathBuf;
@@ -504,6 +504,10 @@ pub fn process(
504504
cx.program(program)?;
505505
}
506506

507+
if let Some(standard) = cx.module.customs.delete_typed::<ast::WebidlBindings>() {
508+
cx.standard(&standard)?;
509+
}
510+
507511
cx.verify()?;
508512

509513
let bindings = cx.module.customs.add(cx.bindings);
@@ -1287,6 +1291,171 @@ impl<'a> Context<'a> {
12871291
Ok(JsImport { name, fields })
12881292
}
12891293

1294+
/// Processes bindings from a standard WebIDL bindings custom section.
1295+
///
1296+
/// No module coming out of the Rust compiler will have one of these, but
1297+
/// eventually there's going to be other producers of the WebIDL bindings
1298+
/// custom section as well. This functionality is intended to allow
1299+
/// `wasm-bindgen`-the-CLI-tool to act as a polyfill for those modules as
1300+
/// well as Rust modules.
1301+
///
1302+
/// Here a standard `WebidlBindings` custom section is taken and we process
1303+
/// that into our own internal data structures to ensure that we have a
1304+
/// binding listed for all the described bindings.
1305+
///
1306+
/// In other words, this is a glorified conversion from the "official"
1307+
/// WebIDL bindings custom section into the wasm-bindgen internal
1308+
/// representation.
1309+
fn standard(&mut self, std: &ast::WebidlBindings) -> Result<(), Error> {
1310+
for (_id, bind) in std.binds.iter() {
1311+
let binding = self.standard_binding(std, bind)?;
1312+
let func = self.module.funcs.get(bind.func);
1313+
match &func.kind {
1314+
walrus::FunctionKind::Import(i) => {
1315+
let id = i.import;
1316+
self.standard_import(binding, id)?;
1317+
}
1318+
walrus::FunctionKind::Local(_) => {
1319+
let export = self
1320+
.module
1321+
.exports
1322+
.iter()
1323+
.find(|e| match e.item {
1324+
walrus::ExportItem::Function(f) => f == bind.func,
1325+
_ => false,
1326+
})
1327+
.ok_or_else(|| format_err!("missing export function for webidl binding"))?;
1328+
let id = export.id();
1329+
self.standard_export(binding, id)?;
1330+
}
1331+
walrus::FunctionKind::Uninitialized(_) => unreachable!(),
1332+
}
1333+
}
1334+
Ok(())
1335+
}
1336+
1337+
/// Creates a wasm-bindgen-internal `Binding` from an official `Bind`
1338+
/// structure specified in the upstream binary format.
1339+
///
1340+
/// This will largely just copy some things into our own arenas but also
1341+
/// processes the list of binding expressions into our own representations.
1342+
fn standard_binding(
1343+
&mut self,
1344+
std: &ast::WebidlBindings,
1345+
bind: &ast::Bind,
1346+
) -> Result<Binding, Error> {
1347+
let binding: &ast::FunctionBinding = std
1348+
.bindings
1349+
.get(bind.binding)
1350+
.ok_or_else(|| format_err!("bad binding id"))?;
1351+
let (wasm_ty, webidl_ty, incoming, outgoing) = match binding {
1352+
ast::FunctionBinding::Export(e) => (
1353+
e.wasm_ty,
1354+
e.webidl_ty,
1355+
e.params.bindings.as_slice(),
1356+
e.result.bindings.as_slice(),
1357+
),
1358+
ast::FunctionBinding::Import(e) => (
1359+
e.wasm_ty,
1360+
e.webidl_ty,
1361+
e.result.bindings.as_slice(),
1362+
e.params.bindings.as_slice(),
1363+
),
1364+
};
1365+
let webidl_ty = copy_ty(&mut self.bindings.types, webidl_ty, &std.types);
1366+
let webidl_ty = match webidl_ty {
1367+
ast::WebidlTypeRef::Id(id) => <ast::WebidlFunction as ast::WebidlTypeId>::wrap(id),
1368+
_ => bail!("invalid webidl type listed"),
1369+
};
1370+
return Ok(Binding {
1371+
wasm_ty,
1372+
webidl_ty,
1373+
incoming: incoming
1374+
.iter()
1375+
.cloned()
1376+
.map(NonstandardIncoming::Standard)
1377+
.collect(),
1378+
outgoing: outgoing
1379+
.iter()
1380+
.cloned()
1381+
.map(NonstandardOutgoing::Standard)
1382+
.collect(),
1383+
return_via_outptr: None,
1384+
});
1385+
1386+
/// Recursively clones `ty` into` dst` where it originally indexes
1387+
/// values in `src`, returning a new type ref which indexes inside of
1388+
/// `dst`.
1389+
fn copy_ty(
1390+
dst: &mut ast::WebidlTypes,
1391+
ty: ast::WebidlTypeRef,
1392+
src: &ast::WebidlTypes,
1393+
) -> ast::WebidlTypeRef {
1394+
let id = match ty {
1395+
ast::WebidlTypeRef::Id(id) => id,
1396+
ast::WebidlTypeRef::Scalar(_) => return ty,
1397+
};
1398+
let ty: &ast::WebidlCompoundType = src.get(id).unwrap();
1399+
match ty {
1400+
ast::WebidlCompoundType::Function(f) => {
1401+
let params = f
1402+
.params
1403+
.iter()
1404+
.map(|param| copy_ty(dst, *param, src))
1405+
.collect();
1406+
let result = f.result.map(|ty| copy_ty(dst, ty, src));
1407+
dst.insert(ast::WebidlFunction {
1408+
kind: f.kind.clone(),
1409+
params,
1410+
result,
1411+
})
1412+
.into()
1413+
}
1414+
_ => unimplemented!(),
1415+
}
1416+
}
1417+
}
1418+
1419+
/// Registers that `id` has a `binding` which was read from a standard
1420+
/// webidl bindings section, so the source of `id` is its actual module/name
1421+
/// listed in the wasm module.
1422+
fn standard_import(&mut self, binding: Binding, id: walrus::ImportId) -> Result<(), Error> {
1423+
let import = self.module.imports.get(id);
1424+
let js = JsImport {
1425+
name: JsImportName::Module {
1426+
module: import.module.clone(),
1427+
name: import.name.clone(),
1428+
},
1429+
fields: Vec::new(),
1430+
};
1431+
let value = AuxValue::Bare(js);
1432+
assert!(self
1433+
.aux
1434+
.import_map
1435+
.insert(id, AuxImport::Value(value))
1436+
.is_none());
1437+
assert!(self.bindings.imports.insert(id, binding).is_none());
1438+
1439+
Ok(())
1440+
}
1441+
1442+
/// Registers that `id` has a `binding` and comes from a standard webidl
1443+
/// bindings section so it doesn't have any documentation or debug names we
1444+
/// can work with.
1445+
fn standard_export(&mut self, binding: Binding, id: walrus::ExportId) -> Result<(), Error> {
1446+
let export = self.module.exports.get(id);
1447+
let kind = AuxExportKind::Function(export.name.clone());
1448+
let export = AuxExport {
1449+
debug_name: format!("standard export {:?}", id),
1450+
comments: String::new(),
1451+
arg_names: None,
1452+
kind,
1453+
};
1454+
assert!(self.aux.export_map.insert(id, export).is_none());
1455+
assert!(self.bindings.exports.insert(id, binding).is_none());
1456+
Ok(())
1457+
}
1458+
12901459
/// Perform a small verification pass over the module to perform some
12911460
/// internal sanity checks.
12921461
fn verify(&self) -> Result<(), Error> {

0 commit comments

Comments
 (0)