2018-05-25 17:51:48 -07:00
|
|
|
/*!
|
|
|
|
# `wasm_bindgen_webidl`
|
|
|
|
|
|
|
|
Converts WebIDL into wasm-bindgen's internal AST form, so that bindings can be
|
|
|
|
emitted for the types and methods described in the WebIDL.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#![deny(missing_docs)]
|
|
|
|
#![deny(missing_debug_implementations)]
|
|
|
|
|
|
|
|
extern crate failure;
|
2018-06-09 21:49:42 -07:00
|
|
|
extern crate heck;
|
2018-06-01 15:24:48 -07:00
|
|
|
#[macro_use]
|
|
|
|
extern crate log;
|
2018-05-25 17:51:48 -07:00
|
|
|
extern crate proc_macro2;
|
2018-05-30 14:30:40 -07:00
|
|
|
extern crate quote;
|
2018-05-25 17:51:48 -07:00
|
|
|
extern crate syn;
|
|
|
|
extern crate wasm_bindgen_backend as backend;
|
|
|
|
extern crate webidl;
|
|
|
|
|
|
|
|
use std::fs;
|
|
|
|
use std::io::{self, Read};
|
2018-06-14 02:03:52 -07:00
|
|
|
use std::iter;
|
2018-05-25 17:51:48 -07:00
|
|
|
use std::path::Path;
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
use failure::ResultExt;
|
|
|
|
use heck::CamelCase;
|
|
|
|
use quote::ToTokens;
|
|
|
|
|
|
|
|
mod util;
|
|
|
|
|
|
|
|
use util::{create_function, ident_ty, raw_ident, rust_ident, webidl_ty_to_syn_ty, TypePosition};
|
|
|
|
|
2018-05-25 17:51:48 -07:00
|
|
|
/// Either `Ok(t)` or `Err(failure::Error)`.
|
|
|
|
pub type Result<T> = ::std::result::Result<T, failure::Error>;
|
|
|
|
|
|
|
|
/// Parse the WebIDL at the given path into a wasm-bindgen AST.
|
|
|
|
pub fn parse_file(webidl_path: &Path) -> Result<backend::ast::Program> {
|
|
|
|
let file = fs::File::open(webidl_path).context("opening WebIDL file")?;
|
|
|
|
let mut file = io::BufReader::new(file);
|
|
|
|
let mut source = String::new();
|
|
|
|
file.read_to_string(&mut source)
|
|
|
|
.context("reading WebIDL file")?;
|
|
|
|
parse(&source)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a string of WebIDL source text into a wasm-bindgen AST.
|
|
|
|
pub fn parse(webidl_source: &str) -> Result<backend::ast::Program> {
|
|
|
|
let definitions = webidl::parse_string(webidl_source).context("parsing WebIDL source text")?;
|
|
|
|
|
|
|
|
let mut program = backend::ast::Program::default();
|
2018-05-30 16:28:57 -07:00
|
|
|
definitions.webidl_parse(&mut program, ())?;
|
2018-05-25 17:51:48 -07:00
|
|
|
|
|
|
|
Ok(program)
|
|
|
|
}
|
|
|
|
|
2018-05-30 14:30:40 -07:00
|
|
|
/// Compile the given WebIDL file into Rust source text containing
|
|
|
|
/// `wasm-bindgen` bindings to the things described in the WebIDL.
|
|
|
|
pub fn compile_file(webidl_path: &Path) -> Result<String> {
|
|
|
|
let ast = parse_file(webidl_path)?;
|
|
|
|
Ok(compile_ast(&ast))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Compile the given WebIDL source text into Rust source text containing
|
|
|
|
/// `wasm-bindgen` bindings to the things described in the WebIDL.
|
|
|
|
pub fn compile(webidl_source: &str) -> Result<String> {
|
|
|
|
let ast = parse(webidl_source)?;
|
|
|
|
Ok(compile_ast(&ast))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn compile_ast(ast: &backend::ast::Program) -> String {
|
|
|
|
let mut tokens = proc_macro2::TokenStream::new();
|
|
|
|
ast.to_tokens(&mut tokens);
|
|
|
|
tokens.to_string()
|
|
|
|
}
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
trait WebidlParse<Ctx> {
|
|
|
|
fn webidl_parse(&self, program: &mut backend::ast::Program, context: Ctx) -> Result<()>;
|
2018-05-25 17:51:48 -07:00
|
|
|
}
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
impl WebidlParse<()> for Vec<webidl::ast::Definition> {
|
2018-05-30 16:28:57 -07:00
|
|
|
fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> {
|
2018-05-25 17:51:48 -07:00
|
|
|
for def in self {
|
2018-05-30 16:28:57 -07:00
|
|
|
def.webidl_parse(program, ())?;
|
2018-05-25 17:51:48 -07:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
impl WebidlParse<()> for webidl::ast::Definition {
|
2018-05-30 16:28:57 -07:00
|
|
|
fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> {
|
2018-05-25 17:51:48 -07:00
|
|
|
match *self {
|
2018-05-30 16:28:57 -07:00
|
|
|
webidl::ast::Definition::Interface(ref interface) => {
|
|
|
|
interface.webidl_parse(program, ())
|
|
|
|
}
|
2018-06-11 18:35:20 -07:00
|
|
|
webidl::ast::Definition::Typedef(ref typedef) => typedef.webidl_parse(program, ()),
|
2018-05-25 17:51:48 -07:00
|
|
|
// TODO
|
|
|
|
webidl::ast::Definition::Callback(..)
|
|
|
|
| webidl::ast::Definition::Dictionary(..)
|
|
|
|
| webidl::ast::Definition::Enum(..)
|
|
|
|
| webidl::ast::Definition::Implements(..)
|
|
|
|
| webidl::ast::Definition::Includes(..)
|
|
|
|
| webidl::ast::Definition::Mixin(..)
|
2018-06-11 18:35:20 -07:00
|
|
|
| webidl::ast::Definition::Namespace(..) => {
|
2018-06-01 15:24:48 -07:00
|
|
|
warn!("Unsupported WebIDL definition: {:?}", self);
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-05-25 17:51:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
impl WebidlParse<()> for webidl::ast::Interface {
|
2018-05-30 16:28:57 -07:00
|
|
|
fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> {
|
2018-05-25 17:51:48 -07:00
|
|
|
match *self {
|
2018-05-30 16:28:57 -07:00
|
|
|
webidl::ast::Interface::NonPartial(ref interface) => {
|
|
|
|
interface.webidl_parse(program, ())
|
|
|
|
}
|
2018-05-25 17:51:48 -07:00
|
|
|
// TODO
|
2018-06-01 15:24:48 -07:00
|
|
|
webidl::ast::Interface::Callback(..) | webidl::ast::Interface::Partial(..) => {
|
|
|
|
warn!("Unsupported WebIDL interface: {:?}", self);
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-05-25 17:51:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
impl WebidlParse<()> for webidl::ast::Typedef {
|
2018-06-11 18:35:20 -07:00
|
|
|
fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> {
|
|
|
|
let dest = rust_ident(&self.name);
|
|
|
|
let src = match webidl_ty_to_syn_ty(&self.type_, TypePosition::Return) {
|
|
|
|
Some(src) => src,
|
|
|
|
None => {
|
|
|
|
warn!(
|
|
|
|
"typedef's source type is not yet supported: {:?}. Skipping typedef {:?}",
|
|
|
|
*self.type_, self
|
|
|
|
);
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
program.type_aliases.push(backend::ast::TypeAlias {
|
|
|
|
vis: syn::Visibility::Public(syn::VisPublic {
|
|
|
|
pub_token: Default::default(),
|
|
|
|
}),
|
|
|
|
dest,
|
|
|
|
src,
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
impl WebidlParse<()> for webidl::ast::NonPartialInterface {
|
2018-05-30 16:28:57 -07:00
|
|
|
fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> {
|
2018-05-25 17:51:48 -07:00
|
|
|
program.imports.push(backend::ast::Import {
|
|
|
|
module: None,
|
|
|
|
version: None,
|
|
|
|
js_namespace: None,
|
|
|
|
kind: backend::ast::ImportKind::Type(backend::ast::ImportType {
|
|
|
|
vis: syn::Visibility::Public(syn::VisPublic {
|
2018-05-30 13:07:20 -07:00
|
|
|
pub_token: Default::default(),
|
2018-05-25 17:51:48 -07:00
|
|
|
}),
|
2018-06-01 16:38:57 -07:00
|
|
|
name: rust_ident(&self.name),
|
2018-05-25 17:51:48 -07:00
|
|
|
}),
|
|
|
|
});
|
2018-05-30 16:28:57 -07:00
|
|
|
|
|
|
|
for member in &self.members {
|
|
|
|
member.webidl_parse(program, &self.name)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
impl<'a> WebidlParse<&'a str> for webidl::ast::InterfaceMember {
|
2018-05-30 16:28:57 -07:00
|
|
|
fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> {
|
|
|
|
match *self {
|
2018-06-10 01:45:23 -07:00
|
|
|
webidl::ast::InterfaceMember::Attribute(ref attr) => {
|
|
|
|
attr.webidl_parse(program, self_name)
|
|
|
|
}
|
2018-05-30 16:28:57 -07:00
|
|
|
webidl::ast::InterfaceMember::Operation(ref op) => op.webidl_parse(program, self_name),
|
|
|
|
// TODO
|
2018-06-10 01:45:23 -07:00
|
|
|
webidl::ast::InterfaceMember::Const(_)
|
2018-05-30 16:28:57 -07:00
|
|
|
| webidl::ast::InterfaceMember::Iterable(_)
|
|
|
|
| webidl::ast::InterfaceMember::Maplike(_)
|
2018-06-01 15:24:48 -07:00
|
|
|
| webidl::ast::InterfaceMember::Setlike(_) => {
|
|
|
|
warn!("Unsupported WebIDL interface member: {:?}", self);
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-05-30 16:28:57 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
impl<'a> WebidlParse<&'a str> for webidl::ast::Attribute {
|
2018-06-10 01:45:23 -07:00
|
|
|
fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> {
|
|
|
|
match *self {
|
|
|
|
webidl::ast::Attribute::Regular(ref attr) => attr.webidl_parse(program, self_name),
|
|
|
|
// TODO
|
|
|
|
webidl::ast::Attribute::Static(_) | webidl::ast::Attribute::Stringifier(_) => {
|
|
|
|
warn!("Unsupported WebIDL attribute: {:?}", self);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
impl<'a> WebidlParse<&'a str> for webidl::ast::Operation {
|
2018-05-30 16:28:57 -07:00
|
|
|
fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> {
|
|
|
|
match *self {
|
|
|
|
webidl::ast::Operation::Regular(ref op) => op.webidl_parse(program, self_name),
|
|
|
|
// TODO
|
|
|
|
webidl::ast::Operation::Special(_)
|
|
|
|
| webidl::ast::Operation::Static(_)
|
2018-06-01 15:24:48 -07:00
|
|
|
| webidl::ast::Operation::Stringifier(_) => {
|
|
|
|
warn!("Unsupported WebIDL operation: {:?}", self);
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-05-30 16:28:57 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute {
|
2018-06-10 01:45:23 -07:00
|
|
|
fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> {
|
|
|
|
fn create_getter(
|
|
|
|
this: &webidl::ast::RegularAttribute,
|
|
|
|
self_name: &str,
|
|
|
|
) -> Option<backend::ast::Import> {
|
2018-06-14 02:03:52 -07:00
|
|
|
let ret = match webidl_ty_to_syn_ty(&this.type_, TypePosition::Return) {
|
2018-06-10 01:45:23 -07:00
|
|
|
None => {
|
|
|
|
warn!("Attribute's type does not yet support reading: {:?}. Skipping getter binding for {:?}",
|
|
|
|
this.type_, this);
|
|
|
|
return None;
|
|
|
|
}
|
2018-06-14 02:03:52 -07:00
|
|
|
Some(ty) => Some(ty),
|
2018-06-10 01:45:23 -07:00
|
|
|
};
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
let kind = backend::ast::ImportFunctionKind::Method {
|
|
|
|
class: self_name.to_string(),
|
|
|
|
ty: ident_ty(rust_ident(self_name)),
|
|
|
|
};
|
2018-06-10 01:45:23 -07:00
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
create_function(
|
|
|
|
&this.name,
|
|
|
|
iter::empty(),
|
|
|
|
kind,
|
|
|
|
ret,
|
|
|
|
vec![backend::ast::BindgenAttr::Getter(Some(raw_ident(
|
|
|
|
&this.name,
|
|
|
|
)))],
|
|
|
|
).map(|function| backend::ast::Import {
|
2018-06-10 01:45:23 -07:00
|
|
|
module: None,
|
|
|
|
version: None,
|
|
|
|
js_namespace: None,
|
2018-06-14 02:03:52 -07:00
|
|
|
kind: backend::ast::ImportKind::Function(function),
|
2018-06-10 01:45:23 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_setter(
|
|
|
|
this: &webidl::ast::RegularAttribute,
|
|
|
|
self_name: &str,
|
|
|
|
) -> Option<backend::ast::Import> {
|
2018-06-14 02:03:52 -07:00
|
|
|
let kind = backend::ast::ImportFunctionKind::Method {
|
|
|
|
class: self_name.to_string(),
|
|
|
|
ty: ident_ty(rust_ident(self_name)),
|
2018-06-10 01:45:23 -07:00
|
|
|
};
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
create_function(
|
|
|
|
&format!("set_{}", this.name.to_camel_case()),
|
|
|
|
iter::once((&*this.name, &*this.type_, false)),
|
|
|
|
kind,
|
|
|
|
None,
|
|
|
|
vec![backend::ast::BindgenAttr::Setter(Some(raw_ident(
|
|
|
|
&this.name,
|
|
|
|
)))],
|
|
|
|
).map(|function| backend::ast::Import {
|
2018-06-10 01:45:23 -07:00
|
|
|
module: None,
|
|
|
|
version: None,
|
|
|
|
js_namespace: None,
|
2018-06-14 02:03:52 -07:00
|
|
|
kind: backend::ast::ImportKind::Function(function),
|
2018-06-10 01:45:23 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
create_getter(self, self_name).map(|import| program.imports.push(import));
|
|
|
|
|
|
|
|
if !self.read_only {
|
|
|
|
create_setter(self, self_name).map(|import| program.imports.push(import));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation {
|
2018-05-30 16:28:57 -07:00
|
|
|
fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> {
|
2018-06-09 21:49:42 -07:00
|
|
|
let name = match self.name {
|
2018-06-01 15:24:48 -07:00
|
|
|
None => {
|
|
|
|
warn!(
|
|
|
|
"Operations without a name are unsupported. Skipping {:?}",
|
|
|
|
self
|
|
|
|
);
|
|
|
|
return Ok(());
|
|
|
|
}
|
2018-06-09 21:49:42 -07:00
|
|
|
Some(ref name) => name,
|
2018-05-30 16:28:57 -07:00
|
|
|
};
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
let kind = backend::ast::ImportFunctionKind::Method {
|
|
|
|
class: self_name.to_string(),
|
|
|
|
ty: ident_ty(rust_ident(self_name)),
|
|
|
|
};
|
2018-06-09 21:49:42 -07:00
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
let ret = match self.return_type {
|
|
|
|
webidl::ast::ReturnType::Void => None,
|
2018-06-01 16:38:57 -07:00
|
|
|
webidl::ast::ReturnType::NonVoid(ref ty) => {
|
|
|
|
match webidl_ty_to_syn_ty(ty, TypePosition::Return) {
|
|
|
|
None => {
|
|
|
|
warn!(
|
2018-06-01 15:24:48 -07:00
|
|
|
"Operation's return type is not yet supported: {:?}. Skipping bindings for {:?}",
|
|
|
|
ty, self
|
|
|
|
);
|
2018-06-01 16:38:57 -07:00
|
|
|
return Ok(());
|
|
|
|
}
|
2018-06-14 02:03:52 -07:00
|
|
|
Some(ty) => Some(ty),
|
2018-06-01 15:24:48 -07:00
|
|
|
}
|
2018-06-01 16:38:57 -07:00
|
|
|
}
|
2018-05-30 16:28:57 -07:00
|
|
|
};
|
|
|
|
|
2018-06-14 02:03:52 -07:00
|
|
|
create_function(
|
|
|
|
&name,
|
|
|
|
self.arguments
|
|
|
|
.iter()
|
|
|
|
.map(|arg| (&*arg.name, &*arg.type_, arg.variadic)),
|
|
|
|
kind,
|
|
|
|
ret,
|
|
|
|
Vec::new(),
|
|
|
|
).map(|function| {
|
|
|
|
program.imports.push(backend::ast::Import {
|
|
|
|
module: None,
|
|
|
|
version: None,
|
|
|
|
js_namespace: None,
|
|
|
|
kind: backend::ast::ImportKind::Function(function),
|
|
|
|
})
|
2018-05-30 16:28:57 -07:00
|
|
|
});
|
|
|
|
|
2018-05-25 17:51:48 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|