Merge pull request #1725 from alexcrichton/real-webidl-section

Add support for emitting a Wasm Interface Types section
This commit is contained in:
Alex Crichton 2019-08-19 06:19:23 -05:00 committed by GitHub
commit 487289cf9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 630 additions and 73 deletions

View File

@ -6,7 +6,7 @@ use walrus::Module;
use wasm_bindgen_anyref_xform::Context;
use wasm_webidl_bindings::ast;
pub fn process(module: &mut Module) -> Result<(), Error> {
pub fn process(module: &mut Module, wasm_interface_types: bool) -> Result<(), Error> {
let mut cfg = Context::default();
cfg.prepare(module)?;
let bindings = module
@ -45,18 +45,24 @@ pub fn process(module: &mut Module) -> Result<(), Error> {
cfg.run(module)?;
// Make sure to export the `anyref` table for the JS bindings since it
// will need to be initialized. If it doesn't exist though then the
// module must not use it, so we skip it.
let table = module.tables.iter().find(|t| match t.kind {
walrus::TableKind::Anyref(_) => true,
_ => false,
});
let table = match table {
Some(t) => t.id(),
None => return Ok(()),
};
module.exports.add("__wbg_anyref_table", table);
// If our output is using WebAssembly interface types then our bindings will
// never use this table, so no need to export it. Otherwise it's highly
// likely in web/JS embeddings this will be used, so make sure we export it
// to avoid it getting gc'd accidentally.
if !wasm_interface_types {
// Make sure to export the `anyref` table for the JS bindings since it
// will need to be initialized. If it doesn't exist though then the
// module must not use it, so we skip it.
let table = module.tables.iter().find(|t| match t.kind {
walrus::TableKind::Anyref(_) => true,
_ => false,
});
let table = match table {
Some(t) => t.id(),
None => return Ok(()),
};
module.exports.add("__wbg_anyref_table", table);
}
// Clean up now-unused intrinsics and shims and such
walrus::passes::gc::run(module);

View File

@ -50,6 +50,15 @@ macro_rules! intrinsics {
)*
}
}
/// Returns the symbol name of this intrinsic
pub fn name(&self) -> &'static str {
match self {
$(
Intrinsic::$name => $sym,
)*
}
}
}
};
}

View File

@ -2081,11 +2081,13 @@ impl<'a> Context<'a> {
&binding.outgoing,
wasm_ty.params(),
&webidl.params,
self.config.wasm_interface_types,
)
&& webidl::incoming_do_not_require_glue(
&binding.incoming,
&webidl.result.into_iter().collect::<Vec<_>>(),
wasm_ty.results(),
self.config.wasm_interface_types,
)
}

View File

@ -36,6 +36,7 @@ pub struct Bindgen {
// "ready to be instantiated on any thread"
threads: wasm_bindgen_threads_xform::Config,
anyref: bool,
wasm_interface_types: bool,
encode_into: EncodeInto,
}
@ -49,6 +50,7 @@ pub struct Output {
snippets: HashMap<String, Vec<String>>,
local_modules: HashMap<String, String>,
npm_dependencies: HashMap<String, (PathBuf, String)>,
wasm_interface_types: bool,
}
#[derive(Clone)]
@ -73,6 +75,8 @@ pub enum EncodeInto {
impl Bindgen {
pub fn new() -> Bindgen {
let anyref = env::var("WASM_BINDGEN_ANYREF").is_ok();
let wasm_interface_types = env::var("WASM_INTERFACE_TYPES").is_ok();
Bindgen {
input: Input::None,
out_name: None,
@ -88,7 +92,8 @@ impl Bindgen {
emit_start: true,
weak_refs: env::var("WASM_BINDGEN_WEAKREF").is_ok(),
threads: threads_config(),
anyref: env::var("WASM_BINDGEN_ANYREF").is_ok(),
anyref: anyref || wasm_interface_types,
wasm_interface_types,
encode_into: EncodeInto::Test,
}
}
@ -315,7 +320,7 @@ impl Bindgen {
// the webidl bindings proposal) as well as an auxiliary section for all
// sorts of miscellaneous information and features #[wasm_bindgen]
// supports that aren't covered by WebIDL bindings.
webidl::process(&mut module, self.anyref, self.emit_start)?;
webidl::process(&mut module, self.anyref, self.wasm_interface_types, self.emit_start)?;
// Now that we've got type information from the webidl processing pass,
// touch up the output of rustc to insert anyref shims where necessary.
@ -323,7 +328,7 @@ impl Bindgen {
// currently off-by-default since `anyref` is still in development in
// engines.
if self.anyref {
anyref::process(&mut module)?;
anyref::process(&mut module, self.wasm_interface_types)?;
}
let aux = module
@ -344,6 +349,11 @@ impl Bindgen {
(npm_dependencies, cx.finalize(stem)?)
};
if self.wasm_interface_types {
webidl::standard::add_section(&mut module, &aux, &bindings)
.with_context(|_| "failed to generate a standard wasm bindings custom section")?;
}
Ok(Output {
module,
stem: stem.to_string(),
@ -354,6 +364,7 @@ impl Bindgen {
ts,
mode: self.mode.clone(),
typescript: self.typescript,
wasm_interface_types: self.wasm_interface_types,
})
}
@ -506,6 +517,7 @@ fn unexported_unused_lld_things(module: &mut Module) {
impl Output {
pub fn js(&self) -> &str {
assert!(!self.wasm_interface_types);
&self.js
}
@ -518,6 +530,23 @@ impl Output {
}
fn _emit(&self, out_dir: &Path) -> Result<(), Error> {
let wasm_name = if self.wasm_interface_types {
self.stem.clone()
} else {
format!("{}_bg", self.stem)
};
let wasm_path = out_dir
.join(wasm_name)
.with_extension("wasm");
fs::create_dir_all(out_dir)?;
let wasm_bytes = self.module.emit_wasm()?;
fs::write(&wasm_path, wasm_bytes)
.with_context(|_| format!("failed to write `{}`", wasm_path.display()))?;
if self.wasm_interface_types {
return Ok(())
}
// Write out all local JS snippets to the final destination now that
// we've collected them from all the programs.
for (identifier, list) in self.snippets.iter() {
@ -554,7 +583,6 @@ impl Output {
} else {
"js"
};
fs::create_dir_all(out_dir)?;
let js_path = out_dir.join(&self.stem).with_extension(extension);
fs::write(&js_path, reset_indentation(&self.js))
.with_context(|_| format!("failed to write `{}`", js_path.display()))?;
@ -565,10 +593,6 @@ impl Output {
.with_context(|_| format!("failed to write `{}`", ts_path.display()))?;
}
let wasm_path = out_dir
.join(format!("{}_bg", self.stem))
.with_extension("wasm");
if self.mode.nodejs() {
let js_path = wasm_path.with_extension(extension);
let shim = self.generate_node_wasm_import(&self.module, &wasm_path);
@ -583,9 +607,6 @@ impl Output {
.with_context(|_| format!("failed to write `{}`", ts_path.display()))?;
}
let wasm_bytes = self.module.emit_wasm()?;
fs::write(&wasm_path, wasm_bytes)
.with_context(|_| format!("failed to write `{}`", wasm_path.display()))?;
Ok(())
}

View File

@ -41,6 +41,7 @@ const PLACEHOLDER_MODULE: &str = "__wbindgen_placeholder__";
mod bindings;
mod incoming;
mod outgoing;
pub mod standard;
pub use self::incoming::NonstandardIncoming;
pub use self::outgoing::NonstandardOutgoing;
@ -474,12 +475,14 @@ struct Context<'a> {
unique_crate_identifier: &'a str,
descriptors: HashMap<String, Descriptor>,
anyref_enabled: bool,
wasm_interface_types: bool,
support_start: bool,
}
pub fn process(
module: &mut Module,
anyref_enabled: bool,
wasm_interface_types: bool,
support_start: bool,
) -> Result<(NonstandardWebidlSectionId, WasmBindgenAuxId), Error> {
let mut storage = Vec::new();
@ -496,6 +499,7 @@ pub fn process(
module,
start_found: false,
anyref_enabled,
wasm_interface_types,
support_start,
};
cx.init()?;
@ -619,8 +623,12 @@ impl<'a> Context<'a> {
// `__wbindgen_init_anyref_table` function. This'll ensure that all
// instances of this module have the initial slots of the anyref table
// initialized correctly.
//
// Note that this is disabled if WebAssembly interface types are enabled
// since that's a slightly different environment for now which doesn't have
// quite the same initialization.
fn inject_anyref_initialization(&mut self) -> Result<(), Error> {
if !self.anyref_enabled {
if !self.anyref_enabled || self.wasm_interface_types {
return Ok(());
}
@ -1345,26 +1353,49 @@ impl<'a> Context<'a> {
.bindings
.get(bind.binding)
.ok_or_else(|| format_err!("bad binding id"))?;
let mut return_via_outptr = None;
let (wasm_ty, webidl_ty, incoming, outgoing) = match binding {
ast::FunctionBinding::Export(e) => (
e.wasm_ty,
e.webidl_ty,
e.params.bindings.as_slice(),
e.result.bindings.as_slice(),
),
ast::FunctionBinding::Import(e) => (
e.wasm_ty,
e.webidl_ty,
e.result.bindings.as_slice(),
e.params.bindings.as_slice(),
),
ast::FunctionBinding::Export(e) => {
// This `match` is weird, see the comment at the top of
// `standard.rs` for what it's doing.
let outgoing = match e.result.bindings.get(0) {
Some(ast::OutgoingBindingExpression::As(a)) if a.idx == u32::max_value() => {
return_via_outptr = Some(vec![walrus::ValType::I32, walrus::ValType::I32]);
&e.result.bindings[1..]
}
_ => &e.result.bindings[..],
};
(
e.wasm_ty,
e.webidl_ty,
e.params.bindings.as_slice(),
outgoing,
)
}
ast::FunctionBinding::Import(e) => {
// This `match` is weird, see the comment at the top of
// `standard.rs` for what it's doing.
let incoming = match e.result.bindings.get(0) {
Some(ast::IncomingBindingExpression::Get(g)) if g.idx == u32::max_value() => {
return_via_outptr = Some(vec![walrus::ValType::I32, walrus::ValType::I32]);
&e.result.bindings[1..]
}
_ => &e.result.bindings[..],
};
(
e.wasm_ty,
e.webidl_ty,
incoming,
e.params.bindings.as_slice(),
)
}
};
let webidl_ty = copy_ty(&mut self.bindings.types, webidl_ty, &std.types);
let webidl_ty = standard::copy_ty(&mut self.bindings.types, webidl_ty, &std.types);
let webidl_ty = match webidl_ty {
ast::WebidlTypeRef::Id(id) => <ast::WebidlFunction as ast::WebidlTypeId>::wrap(id),
_ => bail!("invalid webidl type listed"),
};
return Ok(Binding {
Ok(Binding {
wasm_ty,
webidl_ty,
incoming: incoming
@ -1377,40 +1408,8 @@ impl<'a> Context<'a> {
.cloned()
.map(NonstandardOutgoing::Standard)
.collect(),
return_via_outptr: None,
});
/// Recursively clones `ty` into` dst` where it originally indexes
/// values in `src`, returning a new type ref which indexes inside of
/// `dst`.
fn copy_ty(
dst: &mut ast::WebidlTypes,
ty: ast::WebidlTypeRef,
src: &ast::WebidlTypes,
) -> ast::WebidlTypeRef {
let id = match ty {
ast::WebidlTypeRef::Id(id) => id,
ast::WebidlTypeRef::Scalar(_) => return ty,
};
let ty: &ast::WebidlCompoundType = src.get(id).unwrap();
match ty {
ast::WebidlCompoundType::Function(f) => {
let params = f
.params
.iter()
.map(|param| copy_ty(dst, *param, src))
.collect();
let result = f.result.map(|ty| copy_ty(dst, ty, src));
dst.insert(ast::WebidlFunction {
kind: f.kind.clone(),
params,
result,
})
.into()
}
_ => unimplemented!(),
}
}
return_via_outptr,
})
}
/// Registers that `id` has a `binding` which was read from a standard
@ -1662,7 +1661,23 @@ pub fn incoming_do_not_require_glue(
exprs: &[NonstandardIncoming],
from_webidl_tys: &[ast::WebidlTypeRef],
to_wasm_tys: &[walrus::ValType],
standard_webidl_enabled: bool,
) -> bool {
// If anything is nonstandard, then we're unconditionally going to need a JS
// shim because, well, it's not standard.
if exprs.iter().any(|e| match e {
NonstandardIncoming::Standard(_) => false,
_ => true,
}) {
return false;
}
// If everything is `Standard` and we've actually got WebIDL bindings fully
// enabled, then we don't require any glue at all!
if standard_webidl_enabled {
return true;
}
exprs.len() == from_webidl_tys.len()
&& exprs.len() == to_wasm_tys.len()
&& exprs
@ -1685,7 +1700,19 @@ pub fn outgoing_do_not_require_glue(
exprs: &[NonstandardOutgoing],
from_wasm_tys: &[walrus::ValType],
to_webidl_tys: &[ast::WebidlTypeRef],
standard_webidl_enabled: bool,
) -> bool {
// Same short-circuits as above.
if exprs.iter().any(|e| match e {
NonstandardOutgoing::Standard(_) => false,
_ => true,
}) {
return false;
}
if standard_webidl_enabled {
return true;
}
exprs.len() == from_wasm_tys.len()
&& exprs.len() == to_webidl_tys.len()
&& exprs

View File

@ -0,0 +1,492 @@
//! Support for generating a standard WebIDL custom section
//!
//! This module has all the necessary support for generating a full-fledged
//! standard WebIDL custom section as defined by the `wasm-webidl-bindings`
//! crate. This module also critically assumes that the WebAssembly module
//! being generated **must be standalone**. In this mode all sorts of features
//! supported by `#[wasm_bindgen]` aren't actually supported, such as closures,
//! imports of global js names, js getters/setters, exporting structs, etc.
//! These features may all eventually come to the standard bindings proposal,
//! but it will likely take some time. In the meantime this module simply focuses
//! on taking what's already a valid wasm module and letting it through with a
//! standard WebIDL custom section. All other modules generate an error during
//! this binding process.
//!
//! Note that when this function is called and used we're also not actually
//! generating any JS glue. Any JS glue currently generated is also invalid if
//! the module contains the wasm bindings section and it's actually respected.
// NB: Returning strings is weird
//
// This module has what is currently a pretty gross hack for dealing with
// returning strings. One of the banner features of WebIDL bindings is not
// requiring any language-specific glue to use wasm files and you get all sorts
// of types like integers and strings by default. Note that strings are a huge
// thing here.
//
// Dealing with *incoming* strings is easy enough in that the binding expression
// has an allocator function to call and it's filled in and passed as two
// pointers. Outgoing strings are a little harder, however, for two reasons:
//
// * One is that we need to return two values, which requires multi-value
// * Another is that someone's got to free the string at some point
//
// Rust/wasm-bindgen don't support multi-value, and WebIDL bindings as literally
// spec'd right this red hot second don't support freeing strings that we pass
// out. These both have obvious fixes (supporting multi-value and extending the
// bindings spec to support freeing) but we also want something working right
// this red-hot second.
//
// To get things working we employ a terrible hack where the first bindign
// expression of the result a function may indicate "use a thing that's way off
// in the ether" and that's actually a sentinel for "oh ignore everything else
// and the string is returned through an out-ptr as the first argument". This
// manifests in all sorts of special hacks all over the place to handle this,
// and it's a real bummer.
//
// This is in general just an explainer for the current state of things and
// also a preemptive apology for writing the code to handle this in so many
// places. I, like you, look forward to actually fixing this for real as the
// spec continues to evolve and we implement more in wasm-bindgen.
use crate::descriptor::VectorKind;
use crate::webidl::{AuxExportKind, AuxImport, AuxValue, JsImport, JsImportName};
use crate::webidl::{NonstandardIncoming, NonstandardOutgoing};
use crate::webidl::{NonstandardWebidlSection, WasmBindgenAux};
use failure::{bail, Error, ResultExt};
use walrus::Module;
use wasm_webidl_bindings::ast;
pub fn add_section(
module: &mut Module,
aux: &WasmBindgenAux,
nonstandard: &NonstandardWebidlSection,
) -> Result<(), Error> {
let mut section = ast::WebidlBindings::default();
let WasmBindgenAux {
extra_typescript: _, // ignore this even if it's specified
local_modules,
snippets,
package_jsons,
export_map,
import_map,
imports_with_catch,
imports_with_variadic,
imports_with_assert_no_shim: _, // not relevant for this purpose
enums,
structs,
} = aux;
for (export, binding) in nonstandard.exports.iter() {
// First up make sure this is something that's actually valid to export
// form a vanilla WebAssembly module with WebIDL bindings.
match &export_map[export].kind {
AuxExportKind::Function(_) => {}
AuxExportKind::Constructor(name) => {
bail!(
"cannot export `{}` constructor function when generating \
a standalone WebAssembly module with no JS glue",
name,
);
}
AuxExportKind::Getter { class, field } => {
bail!(
"cannot export `{}::{}` getter function when generating \
a standalone WebAssembly module with no JS glue",
class,
field,
);
}
AuxExportKind::Setter { class, field } => {
bail!(
"cannot export `{}::{}` setter function when generating \
a standalone WebAssembly module with no JS glue",
class,
field,
);
}
AuxExportKind::StaticFunction { class, name } => {
bail!(
"cannot export `{}::{}` static function when \
generating a standalone WebAssembly module with no \
JS glue",
class,
name
);
}
AuxExportKind::Method { class, name, .. } => {
bail!(
"cannot export `{}::{}` method when \
generating a standalone WebAssembly module with no \
JS glue",
class,
name
);
}
}
let name = &module.exports.get(*export).name;
let params = extract_incoming(&binding.incoming).with_context(|_| {
format!(
"failed to map arguments for export `{}` to standard \
binding expressions",
name
)
})?;
let mut result = extract_outgoing(&binding.outgoing).with_context(|_| {
format!(
"failed to map return value for export `{}` to standard \
binding expressions",
name
)
})?;
// see comment at top of this module about returning strings for what
// this is doing and why it's weird
if binding.return_via_outptr.is_some() {
result.insert(
0,
ast::OutgoingBindingExpressionAs {
idx: u32::max_value(),
ty: ast::WebidlScalarType::Long.into(),
}
.into(),
);
}
let binding = section.bindings.insert(ast::ExportBinding {
wasm_ty: binding.wasm_ty,
webidl_ty: copy_ty(
&mut section.types,
binding.webidl_ty.into(),
&nonstandard.types,
),
params: ast::IncomingBindingMap { bindings: params },
result: ast::OutgoingBindingMap { bindings: result },
});
let func = match module.exports.get(*export).item {
walrus::ExportItem::Function(f) => f,
_ => unreachable!(),
};
section.binds.insert(ast::Bind {
func,
binding: binding.into(),
});
}
for (import, binding) in nonstandard.imports.iter() {
check_standard_import(&import_map[import])?;
let (module_name, name) = {
let import = module.imports.get(*import);
(&import.module, &import.name)
};
let params = extract_outgoing(&binding.outgoing).with_context(|_| {
format!(
"failed to map arguments of import `{}::{}` to standard \
binding expressions",
module_name, name,
)
})?;
let mut result = extract_incoming(&binding.incoming).with_context(|_| {
format!(
"failed to map return value of import `{}::{}` to standard \
binding expressions",
module_name, name,
)
})?;
// see comment at top of this module about returning strings for what
// this is doing and why it's weird
if binding.return_via_outptr.is_some() {
result.insert(
0,
ast::IncomingBindingExpressionGet {
idx: u32::max_value(),
}
.into(),
);
}
let binding = section.bindings.insert(ast::ImportBinding {
wasm_ty: binding.wasm_ty,
webidl_ty: copy_ty(
&mut section.types,
binding.webidl_ty.into(),
&nonstandard.types,
),
params: ast::OutgoingBindingMap { bindings: params },
result: ast::IncomingBindingMap { bindings: result },
});
let func = match module.imports.get(*import).kind {
walrus::ImportKind::Function(f) => f,
_ => unreachable!(),
};
section.binds.insert(ast::Bind {
func,
binding: binding.into(),
});
}
if let Some((name, _)) = local_modules.iter().next() {
bail!(
"generating a bindings section is currently incompatible with \
local JS modules being specified as well, `{}` cannot be used \
since a standalone wasm file is being generated",
name,
);
}
if let Some((name, _)) = snippets.iter().filter(|(_, v)| !v.is_empty()).next() {
bail!(
"generating a bindings section is currently incompatible with \
local JS snippets being specified as well, `{}` cannot be used \
since a standalone wasm file is being generated",
name,
);
}
if let Some(path) = package_jsons.iter().next() {
bail!(
"generating a bindings section is currently incompatible with \
package.json being consumed as well, `{}` cannot be used \
since a standalone wasm file is being generated",
path.display(),
);
}
if let Some(import) = imports_with_catch.iter().next() {
let import = module.imports.get(*import);
bail!(
"generating a bindings section is currently incompatible with \
`#[wasm_bindgen(catch)]` on the `{}::{}` import because a \
a standalone wasm file is being generated",
import.module,
import.name,
);
}
if let Some(import) = imports_with_variadic.iter().next() {
let import = module.imports.get(*import);
bail!(
"generating a bindings section is currently incompatible with \
`#[wasm_bindgen(variadic)]` on the `{}::{}` import because a \
a standalone wasm file is being generated",
import.module,
import.name,
);
}
if let Some(enum_) = enums.iter().next() {
bail!(
"generating a bindings section is currently incompatible with \
exporting an `enum` from the wasm file, cannot export `{}`",
enum_.name,
);
}
if let Some(struct_) = structs.iter().next() {
bail!(
"generating a bindings section is currently incompatible with \
exporting a `struct` from the wasm file, cannot export `{}`",
struct_.name,
);
}
if nonstandard.elems.len() > 0 {
// Note that this is a pretty cryptic error message, but we in theory
// shouldn't ever hit this since closures always show up as some form
// of nonstandard binding which was previously checked.
bail!("generating a standalone wasm file requires no table element bindings");
}
module.customs.add(section);
Ok(())
}
fn extract_incoming(
nonstandard: &[NonstandardIncoming],
) -> Result<Vec<ast::IncomingBindingExpression>, Error> {
let mut exprs = Vec::new();
for expr in nonstandard {
let desc = match expr {
NonstandardIncoming::Standard(e) => {
exprs.push(e.clone());
continue;
}
NonstandardIncoming::Int64 { .. } => "64-bit integer",
NonstandardIncoming::AllocCopyInt64 { .. } => "64-bit integer array",
NonstandardIncoming::AllocCopyAnyrefArray { .. } => "array of JsValue",
NonstandardIncoming::MutableSlice { .. } => "mutable slice",
NonstandardIncoming::OptionSlice { .. } => "optional slice",
NonstandardIncoming::OptionVector { .. } => "optional vector",
NonstandardIncoming::OptionAnyref { .. } => "optional anyref",
NonstandardIncoming::OptionNative { .. } => "optional integer",
NonstandardIncoming::OptionU32Sentinel { .. } => "optional integer",
NonstandardIncoming::OptionBool { .. } => "optional bool",
NonstandardIncoming::OptionChar { .. } => "optional char",
NonstandardIncoming::OptionIntegerEnum { .. } => "optional enum",
NonstandardIncoming::OptionInt64 { .. } => "optional integer",
NonstandardIncoming::RustType { .. } => "native Rust type",
NonstandardIncoming::RustTypeRef { .. } => "reference to Rust type",
NonstandardIncoming::OptionRustType { .. } => "optional Rust type",
NonstandardIncoming::Char { .. } => "character",
NonstandardIncoming::BorrowedAnyref { .. } => "borrowed anyref",
};
bail!("cannot represent {} with a standard bindings expression", desc);
}
Ok(exprs)
}
fn extract_outgoing(
nonstandard: &[NonstandardOutgoing],
) -> Result<Vec<ast::OutgoingBindingExpression>, Error> {
let mut exprs = Vec::new();
for expr in nonstandard {
let desc = match expr {
NonstandardOutgoing::Standard(e) => {
exprs.push(e.clone());
continue;
}
// ... yeah ... let's just leak strings
// see comment at top of this module about returning strings for
// what this is doing and why it's weird
NonstandardOutgoing::Vector {
offset,
length,
kind: VectorKind::String,
} => {
exprs.push(
ast::OutgoingBindingExpressionUtf8Str {
offset: *offset,
length: *length,
ty: ast::WebidlScalarType::DomString.into(),
}
.into(),
);
continue;
}
NonstandardOutgoing::RustType { .. } => "rust type",
NonstandardOutgoing::Char { .. } => "character",
NonstandardOutgoing::Number64 { .. } => "64-bit integer",
NonstandardOutgoing::BorrowedAnyref { .. } => "borrowed anyref",
NonstandardOutgoing::Vector { .. } => "vector",
NonstandardOutgoing::CachedString { .. } => "cached string",
NonstandardOutgoing::View64 { .. } => "64-bit slice",
NonstandardOutgoing::ViewAnyref { .. } => "anyref slice",
NonstandardOutgoing::OptionVector { .. } => "optional vector",
NonstandardOutgoing::OptionSlice { .. } => "optional slice",
NonstandardOutgoing::OptionNative { .. } => "optional integer",
NonstandardOutgoing::OptionU32Sentinel { .. } => "optional integer",
NonstandardOutgoing::OptionBool { .. } => "optional boolean",
NonstandardOutgoing::OptionChar { .. } => "optional character",
NonstandardOutgoing::OptionIntegerEnum { .. } => "optional enum",
NonstandardOutgoing::OptionInt64 { .. } => "optional 64-bit integer",
NonstandardOutgoing::OptionRustType { .. } => "optional rust type",
NonstandardOutgoing::StackClosure { .. } => "closures",
};
bail!("cannot represent {} with a standard bindings expression", desc);
}
Ok(exprs)
}
/// Recursively clones `ty` into` dst` where it originally indexes values in
/// `src`, returning a new type ref which indexes inside of `dst`.
pub fn copy_ty(
dst: &mut ast::WebidlTypes,
ty: ast::WebidlTypeRef,
src: &ast::WebidlTypes,
) -> ast::WebidlTypeRef {
let id = match ty {
ast::WebidlTypeRef::Id(id) => id,
ast::WebidlTypeRef::Scalar(_) => return ty,
};
let ty: &ast::WebidlCompoundType = src.get(id).unwrap();
match ty {
ast::WebidlCompoundType::Function(f) => {
let params = f
.params
.iter()
.map(|param| copy_ty(dst, *param, src))
.collect();
let result = f.result.map(|ty| copy_ty(dst, ty, src));
dst.insert(ast::WebidlFunction {
kind: f.kind.clone(),
params,
result,
})
.into()
}
_ => unimplemented!(),
}
}
fn check_standard_import(import: &AuxImport) -> Result<(), Error> {
let desc_js = |js: &JsImport| {
let mut extra = String::new();
for field in js.fields.iter() {
extra.push_str(".");
extra.push_str(field);
}
match &js.name {
JsImportName::Global { name } | JsImportName::VendorPrefixed { name, .. } => {
format!("global `{}{}`", name, extra)
}
JsImportName::Module { module, name } => {
format!("`{}{}` from '{}'", name, extra, module)
}
JsImportName::LocalModule { module, name } => {
format!("`{}{}` from local module '{}'", name, extra, module)
}
JsImportName::InlineJs {
unique_crate_identifier,
name,
..
} => format!(
"`{}{}` from inline js in '{}'",
name, extra, unique_crate_identifier
),
}
};
let item = match import {
AuxImport::Value(AuxValue::Bare(js)) => {
if js.fields.len() == 0 {
if let JsImportName::Module { .. } = js.name {
return Ok(());
}
}
desc_js(js)
}
AuxImport::Value(AuxValue::Getter(js, name))
| AuxImport::Value(AuxValue::Setter(js, name))
| AuxImport::Value(AuxValue::ClassGetter(js, name))
| AuxImport::Value(AuxValue::ClassSetter(js, name)) => {
format!("field access of `{}` for {}", name, desc_js(js))
}
AuxImport::Instanceof(js) => format!("instance of check of {}", desc_js(js)),
AuxImport::Static(js) => format!("static js value {}", desc_js(js)),
AuxImport::StructuralMethod(name) => format!("structural method `{}`", name),
AuxImport::StructuralGetter(name)
| AuxImport::StructuralSetter(name)
| AuxImport::StructuralClassGetter(_, name)
| AuxImport::StructuralClassSetter(_, name) => {
format!("structural field access of `{}`", name)
}
AuxImport::IndexingDeleterOfClass(_)
| AuxImport::IndexingDeleterOfObject
| AuxImport::IndexingGetterOfClass(_)
| AuxImport::IndexingGetterOfObject
| AuxImport::IndexingSetterOfClass(_)
| AuxImport::IndexingSetterOfObject => format!("indexing getters/setters/deleters"),
AuxImport::WrapInExportedClass(name) => {
format!("wrapping a pointer in a `{}` js class wrapper", name)
}
AuxImport::Intrinsic(intrinsic) => {
format!("wasm-bindgen specific intrinsic `{}`", intrinsic.name())
}
AuxImport::Closure { .. } => format!("creating a `Closure` wrapper"),
};
bail!(
"cannot generate a standalone WebAssembly module which \
contains an import of {} since it requires JS glue",
item
);
}