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)]
|
2018-07-19 14:57:04 -05:00
|
|
|
#![doc(html_root_url = "https://docs.rs/wasm-bindgen-webidl/0.2")]
|
2018-05-25 17:51:48 -07:00
|
|
|
|
2018-08-03 14:39:33 -07:00
|
|
|
#[macro_use]
|
2018-05-25 17:51:48 -07:00
|
|
|
extern crate failure;
|
2018-07-26 18:09:04 +01:00
|
|
|
#[macro_use]
|
|
|
|
extern crate failure_derive;
|
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-07-23 11:04:28 -04:00
|
|
|
#[macro_use]
|
2018-05-30 14:30:40 -07:00
|
|
|
extern crate quote;
|
2018-07-23 11:04:28 -04:00
|
|
|
#[macro_use]
|
2018-05-25 17:51:48 -07:00
|
|
|
extern crate syn;
|
|
|
|
extern crate wasm_bindgen_backend as backend;
|
2018-08-03 14:39:33 -07:00
|
|
|
extern crate weedle;
|
2018-05-25 17:51:48 -07:00
|
|
|
|
2018-07-13 21:46:36 -07:00
|
|
|
mod first_pass;
|
2018-08-11 23:46:33 +03:00
|
|
|
mod idl_type;
|
2018-06-25 10:41:33 -07:00
|
|
|
mod util;
|
2018-07-26 18:09:04 +01:00
|
|
|
mod error;
|
2018-06-25 10:41:33 -07:00
|
|
|
|
2018-07-09 16:35:25 -07:00
|
|
|
use std::collections::BTreeSet;
|
2018-05-25 17:51:48 -07:00
|
|
|
use std::fs;
|
|
|
|
use std::io::{self, Read};
|
2018-07-26 18:09:04 +01:00
|
|
|
use std::iter::FromIterator;
|
2018-05-25 17:51:48 -07:00
|
|
|
use std::path::Path;
|
|
|
|
|
2018-08-14 10:16:18 -07:00
|
|
|
use backend::ast;
|
2018-08-01 17:15:27 -05:00
|
|
|
use backend::TryToTokens;
|
2018-07-09 16:35:25 -07:00
|
|
|
use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports};
|
2018-08-14 10:16:18 -07:00
|
|
|
use backend::defined::ImportedTypeReferences;
|
2018-08-11 12:38:58 +01:00
|
|
|
use backend::util::{ident_ty, rust_ident, raw_ident, wrap_import_function};
|
2018-08-03 14:39:33 -07:00
|
|
|
use failure::ResultExt;
|
2018-08-10 17:06:11 +01:00
|
|
|
use heck::{ShoutySnakeCase, SnakeCase};
|
2018-08-04 10:00:30 -07:00
|
|
|
use proc_macro2::{Ident, Span};
|
2018-08-03 14:39:33 -07:00
|
|
|
use weedle::argument::Argument;
|
2018-08-28 16:32:31 -07:00
|
|
|
use weedle::attribute::{ExtendedAttributeList};
|
2018-08-14 10:16:18 -07:00
|
|
|
use weedle::dictionary::DictionaryMember;
|
2018-06-14 02:03:52 -07:00
|
|
|
|
2018-08-29 17:33:35 -07:00
|
|
|
use first_pass::{FirstPass, FirstPassRecord, OperationId, InterfaceData};
|
|
|
|
use first_pass::OperationData2;
|
2018-08-03 14:39:33 -07:00
|
|
|
use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc};
|
2018-08-11 23:46:33 +03:00
|
|
|
use idl_type::{IdlType, ToIdlType};
|
2018-06-14 02:03:52 -07:00
|
|
|
|
2018-07-26 18:09:04 +01:00
|
|
|
pub use error::{Error, ErrorKind, Result};
|
2018-05-25 17:51:48 -07:00
|
|
|
|
|
|
|
/// Parse the WebIDL at the given path into a wasm-bindgen AST.
|
2018-07-09 16:35:25 -07:00
|
|
|
fn parse_file(webidl_path: &Path) -> Result<backend::ast::Program> {
|
2018-07-26 18:09:04 +01:00
|
|
|
let file = fs::File::open(webidl_path).context(ErrorKind::OpeningWebIDLFile)?;
|
2018-05-25 17:51:48 -07:00
|
|
|
let mut file = io::BufReader::new(file);
|
|
|
|
let mut source = String::new();
|
2018-07-26 18:09:04 +01:00
|
|
|
file.read_to_string(&mut source).context(ErrorKind::ReadingWebIDLFile)?;
|
2018-05-25 17:51:48 -07:00
|
|
|
parse(&source)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a string of WebIDL source text into a wasm-bindgen AST.
|
2018-07-09 16:35:25 -07:00
|
|
|
fn parse(webidl_source: &str) -> Result<backend::ast::Program> {
|
2018-08-03 14:39:33 -07:00
|
|
|
let definitions = match weedle::parse(webidl_source) {
|
2018-07-26 18:09:04 +01:00
|
|
|
Ok(def) => def,
|
|
|
|
Err(e) => {
|
2018-08-03 14:39:33 -07:00
|
|
|
return Err(match &e {
|
|
|
|
weedle::Err::Incomplete(needed) => {
|
|
|
|
format_err!("needed {:?} more bytes", needed)
|
|
|
|
.context(ErrorKind::ParsingWebIDLSource).into()
|
2018-07-26 18:09:04 +01:00
|
|
|
}
|
2018-08-03 14:39:33 -07:00
|
|
|
weedle::Err::Error(cx) |
|
|
|
|
weedle::Err::Failure(cx) => {
|
|
|
|
let remaining = match cx {
|
|
|
|
weedle::Context::Code(remaining, _) => remaining,
|
|
|
|
};
|
|
|
|
let pos = webidl_source.len() - remaining.len();
|
|
|
|
format_err!("failed to parse WebIDL")
|
|
|
|
.context(ErrorKind::ParsingWebIDLSourcePos(pos)).into()
|
2018-07-26 18:09:04 +01:00
|
|
|
}
|
2018-08-03 14:39:33 -07:00
|
|
|
});
|
2018-07-26 18:09:04 +01:00
|
|
|
}
|
|
|
|
};
|
2018-05-25 17:51:48 -07:00
|
|
|
|
2018-07-13 21:46:36 -07:00
|
|
|
let mut first_pass_record = Default::default();
|
2018-07-30 17:41:22 +03:00
|
|
|
definitions.first_pass(&mut first_pass_record, ())?;
|
2018-07-13 21:46:36 -07:00
|
|
|
let mut program = Default::default();
|
2018-08-28 16:32:31 -07:00
|
|
|
|
|
|
|
for e in first_pass_record.enums.values() {
|
|
|
|
first_pass_record.append_enum(&mut program, e);
|
|
|
|
}
|
|
|
|
for d in first_pass_record.dictionaries.values() {
|
|
|
|
first_pass_record.append_dictionary(&mut program, d);
|
|
|
|
}
|
|
|
|
for (name, n) in first_pass_record.namespaces.iter() {
|
|
|
|
first_pass_record.append_ns(&mut program, name, n);
|
|
|
|
}
|
|
|
|
for (name, d) in first_pass_record.interfaces.iter() {
|
|
|
|
first_pass_record.append_interface(&mut program, name, d);
|
|
|
|
}
|
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)?;
|
2018-07-09 16:35:25 -07:00
|
|
|
Ok(compile_ast(ast))
|
2018-05-30 14:30:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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)?;
|
2018-07-09 16:35:25 -07:00
|
|
|
Ok(compile_ast(ast))
|
2018-05-30 14:30:40 -07:00
|
|
|
}
|
|
|
|
|
2018-07-25 11:42:01 +01:00
|
|
|
/// Run codegen on the AST to generate rust code.
|
2018-07-09 16:35:25 -07:00
|
|
|
fn compile_ast(mut ast: backend::ast::Program) -> String {
|
2018-08-14 10:16:18 -07:00
|
|
|
// Iteratively prune all entries from the AST which reference undefined
|
|
|
|
// fields. Each pass may remove definitions of types and so we need to
|
|
|
|
// reexecute this pass to see if we need to keep removing types until we
|
|
|
|
// reach a steady state.
|
|
|
|
let builtin = BTreeSet::from_iter(
|
2018-07-09 16:35:25 -07:00
|
|
|
vec![
|
|
|
|
"str", "char", "bool", "JsValue", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64",
|
2018-07-24 15:00:46 +01:00
|
|
|
"usize", "isize", "f32", "f64", "Result", "String", "Vec", "Option",
|
2018-08-14 15:46:59 -07:00
|
|
|
"ArrayBuffer", "Object", "Promise",
|
2018-07-09 16:35:25 -07:00
|
|
|
].into_iter()
|
|
|
|
.map(|id| proc_macro2::Ident::new(id, proc_macro2::Span::call_site())),
|
|
|
|
);
|
2018-08-14 10:16:18 -07:00
|
|
|
loop {
|
|
|
|
let mut defined = builtin.clone();
|
|
|
|
ast.imported_type_definitions(&mut |id| {
|
|
|
|
defined.insert(id.clone());
|
|
|
|
});
|
|
|
|
if !ast.remove_undefined_imports(&|id| defined.contains(id)) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2018-07-09 16:35:25 -07:00
|
|
|
|
2018-05-30 14:30:40 -07:00
|
|
|
let mut tokens = proc_macro2::TokenStream::new();
|
2018-08-01 17:15:27 -05:00
|
|
|
if let Err(e) = ast.try_to_tokens(&mut tokens) {
|
|
|
|
e.panic();
|
|
|
|
}
|
2018-05-30 14:30:40 -07:00
|
|
|
tokens.to_string()
|
|
|
|
}
|
|
|
|
|
2018-08-28 16:32:31 -07:00
|
|
|
impl<'src> FirstPassRecord<'src> {
|
|
|
|
fn append_enum(
|
2018-07-10 22:59:59 -07:00
|
|
|
&self,
|
|
|
|
program: &mut backend::ast::Program,
|
2018-08-28 16:32:31 -07:00
|
|
|
enum_: &'src weedle::EnumDefinition<'src>
|
2018-08-13 18:59:52 +03:00
|
|
|
) {
|
2018-08-28 16:32:31 -07:00
|
|
|
let variants = &enum_.values.body.list;
|
2018-07-08 22:09:00 -04:00
|
|
|
program.imports.push(backend::ast::Import {
|
|
|
|
module: None,
|
|
|
|
js_namespace: None,
|
|
|
|
kind: backend::ast::ImportKind::Enum(backend::ast::ImportEnum {
|
2018-07-10 22:59:59 -07:00
|
|
|
vis: public(),
|
2018-08-28 16:32:31 -07:00
|
|
|
name: rust_ident(camel_case_ident(enum_.identifier.0).as_str()),
|
2018-08-03 14:39:33 -07:00
|
|
|
variants: variants
|
2018-07-08 22:09:00 -04:00
|
|
|
.iter()
|
2018-08-03 14:39:33 -07:00
|
|
|
.map(|v| {
|
2018-08-09 21:38:37 +01:00
|
|
|
if !v.0.is_empty() {
|
|
|
|
rust_ident(camel_case_ident(&v.0).as_str())
|
2018-08-04 00:19:06 +03:00
|
|
|
} else {
|
|
|
|
rust_ident("None")
|
|
|
|
}
|
2018-08-03 14:39:33 -07:00
|
|
|
})
|
2018-07-08 22:09:00 -04:00
|
|
|
.collect(),
|
2018-08-03 14:39:33 -07:00
|
|
|
variant_values: variants.iter().map(|v| v.0.to_string()).collect(),
|
2018-07-23 11:04:28 -04:00
|
|
|
rust_attrs: vec![parse_quote!(#[derive(Copy, Clone, PartialEq, Debug)])],
|
2018-07-08 22:09:00 -04:00
|
|
|
}),
|
|
|
|
});
|
2018-07-13 06:04:40 +02:00
|
|
|
}
|
2018-08-09 21:38:37 +01:00
|
|
|
|
2018-08-28 16:32:31 -07:00
|
|
|
// tons more data for what's going on here at
|
|
|
|
// https://www.w3.org/TR/WebIDL-1/#idl-dictionaries
|
|
|
|
fn append_dictionary(
|
|
|
|
&self,
|
2018-08-10 17:06:11 +01:00
|
|
|
program: &mut backend::ast::Program,
|
2018-08-29 15:00:58 -07:00
|
|
|
data: &first_pass::DictionaryData<'src>,
|
2018-08-28 16:32:31 -07:00
|
|
|
) {
|
|
|
|
let def = match data.definition {
|
|
|
|
Some(def) => def,
|
|
|
|
None => return,
|
2018-08-10 19:00:56 +01:00
|
|
|
};
|
2018-08-14 10:16:18 -07:00
|
|
|
let mut fields = Vec::new();
|
2018-08-29 10:40:17 -07:00
|
|
|
if !self.append_dictionary_members(def.identifier.0, &mut fields) {
|
2018-08-28 16:32:31 -07:00
|
|
|
return
|
2018-08-14 10:16:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
program.dictionaries.push(ast::Dictionary {
|
2018-08-28 16:32:31 -07:00
|
|
|
name: rust_ident(&camel_case_ident(def.identifier.0)),
|
2018-08-14 10:16:18 -07:00
|
|
|
fields,
|
|
|
|
});
|
2018-08-29 10:40:17 -07:00
|
|
|
}
|
2018-08-14 10:16:18 -07:00
|
|
|
|
2018-08-29 10:40:17 -07:00
|
|
|
fn append_dictionary_members(
|
|
|
|
&self,
|
|
|
|
dict: &'src str,
|
|
|
|
dst: &mut Vec<ast::DictionaryField>,
|
|
|
|
) -> bool {
|
|
|
|
let dict_data = &self.dictionaries[&dict];
|
|
|
|
let definition = dict_data.definition.unwrap();
|
|
|
|
|
|
|
|
// > The order of the dictionary members on a given dictionary is
|
|
|
|
// > such that inherited dictionary members are ordered before
|
|
|
|
// > non-inherited members ...
|
|
|
|
if let Some(parent) = &definition.inheritance {
|
|
|
|
if !self.append_dictionary_members(parent.identifier.0, dst) {
|
|
|
|
return false
|
2018-08-14 10:16:18 -07:00
|
|
|
}
|
2018-08-29 10:40:17 -07:00
|
|
|
}
|
2018-08-14 10:16:18 -07:00
|
|
|
|
2018-08-29 10:40:17 -07:00
|
|
|
// > ... and the dictionary members on the one dictionary
|
|
|
|
// > definition (including any partial dictionary definitions) are
|
|
|
|
// > ordered lexicographically by the Unicode codepoints that
|
|
|
|
// > comprise their identifiers.
|
|
|
|
let start = dst.len();
|
|
|
|
let members = definition.members.body.iter();
|
|
|
|
let partials = dict_data.partials.iter().flat_map(|d| &d.members.body);
|
|
|
|
for member in members.chain(partials) {
|
|
|
|
match self.dictionary_field(member) {
|
|
|
|
Some(f) => dst.push(f),
|
|
|
|
None => {
|
|
|
|
warn!(
|
|
|
|
"unsupported dictionary field {:?}",
|
|
|
|
(dict, member.identifier.0),
|
|
|
|
);
|
|
|
|
// If this is required then we can't support the
|
|
|
|
// dictionary at all, but if it's not required we can
|
|
|
|
// avoid generating bindings for the field and keep
|
|
|
|
// going otherwise.
|
|
|
|
if member.required.is_some() {
|
|
|
|
return false
|
2018-08-14 10:16:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-29 10:40:17 -07:00
|
|
|
// Note that this sort isn't *quite* right in that it is sorting
|
|
|
|
// based on snake case instead of the original casing which could
|
|
|
|
// produce inconsistent results, but should work well enough for
|
|
|
|
// now!
|
|
|
|
dst[start..].sort_by_key(|f| f.name.clone());
|
2018-08-14 10:16:18 -07:00
|
|
|
|
2018-08-29 10:40:17 -07:00
|
|
|
return true
|
|
|
|
}
|
2018-08-14 10:16:18 -07:00
|
|
|
|
2018-08-29 10:40:17 -07:00
|
|
|
fn dictionary_field(
|
|
|
|
&self,
|
|
|
|
field: &'src DictionaryMember<'src>,
|
|
|
|
) -> Option<ast::DictionaryField> {
|
|
|
|
// use argument position now as we're just binding setters
|
|
|
|
let ty = field.type_.to_idl_type(self)?.to_syn_type(TypePosition::Argument)?;
|
|
|
|
|
|
|
|
// Slice types aren't supported because they don't implement
|
|
|
|
// `Into<JsValue>`
|
|
|
|
if let syn::Type::Reference(ty) = &ty {
|
|
|
|
match &*ty.elem {
|
|
|
|
syn::Type::Slice(_) => return None,
|
|
|
|
_ => {}
|
2018-08-14 10:16:18 -07:00
|
|
|
}
|
2018-08-29 10:40:17 -07:00
|
|
|
}
|
2018-08-14 10:16:18 -07:00
|
|
|
|
2018-08-29 10:40:17 -07:00
|
|
|
// Similarly i64/u64 aren't supported because they don't
|
|
|
|
// implement `Into<JsValue>`
|
|
|
|
let mut any_64bit = false;
|
|
|
|
ty.imported_type_references(&mut |i| {
|
|
|
|
any_64bit = any_64bit || i == "u64" || i == "i64";
|
|
|
|
});
|
|
|
|
if any_64bit {
|
|
|
|
return None
|
2018-08-14 10:16:18 -07:00
|
|
|
}
|
2018-08-29 10:40:17 -07:00
|
|
|
|
|
|
|
Some(ast::DictionaryField {
|
|
|
|
required: field.required.is_some(),
|
|
|
|
name: rust_ident(&field.identifier.0.to_snake_case()),
|
|
|
|
ty,
|
|
|
|
})
|
2018-08-14 10:16:18 -07:00
|
|
|
}
|
2018-08-28 16:32:31 -07:00
|
|
|
|
|
|
|
fn append_ns(
|
|
|
|
&'src self,
|
|
|
|
program: &mut backend::ast::Program,
|
|
|
|
name: &'src str,
|
|
|
|
ns: &'src first_pass::NamespaceData<'src>,
|
|
|
|
) {
|
|
|
|
let mut module = backend::ast::Module {
|
|
|
|
vis: public(),
|
|
|
|
name: rust_ident(name.to_snake_case().as_str()),
|
|
|
|
imports: Default::default(),
|
|
|
|
};
|
|
|
|
|
2018-08-29 15:00:58 -07:00
|
|
|
for (id, data) in ns.operations2.iter() {
|
|
|
|
self.append_ns_member(&mut module, name, id, data);
|
2018-08-28 16:32:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
program.modules.push(module);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn append_ns_member(
|
|
|
|
&self,
|
|
|
|
module: &mut backend::ast::Module,
|
|
|
|
self_name: &'src str,
|
2018-08-29 15:00:58 -07:00
|
|
|
id: &OperationId<'src>,
|
2018-08-29 17:33:35 -07:00
|
|
|
data: &OperationData2<'src>,
|
2018-08-28 16:32:31 -07:00
|
|
|
) {
|
2018-08-29 15:00:58 -07:00
|
|
|
let name = match id {
|
|
|
|
OperationId::Operation(Some(name)) => name,
|
2018-08-29 18:27:49 -07:00
|
|
|
OperationId::Constructor(_) |
|
2018-08-29 15:00:58 -07:00
|
|
|
OperationId::Operation(None) |
|
|
|
|
OperationId::IndexingGetter |
|
|
|
|
OperationId::IndexingSetter |
|
|
|
|
OperationId::IndexingDeleter => {
|
|
|
|
warn!("Unsupported unnamed operation: on {:?}", self_name);
|
|
|
|
return
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let doc_comment = format!(
|
|
|
|
"The `{}.{}()` function\n\n{}",
|
2018-08-28 16:32:31 -07:00
|
|
|
self_name,
|
2018-08-29 15:00:58 -07:00
|
|
|
name,
|
|
|
|
mdn_doc(self_name, Some(&name))
|
|
|
|
);
|
|
|
|
|
|
|
|
let kind = backend::ast::ImportFunctionKind::Normal;
|
|
|
|
for mut import_function in self.create_imports(kind, id, data) {
|
|
|
|
import_function.doc_comment = Some(doc_comment.clone());
|
2018-08-28 16:32:31 -07:00
|
|
|
module.imports.push(
|
|
|
|
backend::ast::Import {
|
|
|
|
module: None,
|
|
|
|
js_namespace: Some(raw_ident(self_name)),
|
|
|
|
kind: backend::ast::ImportKind::Function(import_function),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn append_const(
|
|
|
|
&self,
|
|
|
|
program: &mut backend::ast::Program,
|
|
|
|
self_name: &'src str,
|
|
|
|
member: &'src weedle::interface::ConstMember<'src>,
|
|
|
|
) {
|
|
|
|
let idl_type = match member.const_type.to_idl_type(self) {
|
|
|
|
Some(idl_type) => idl_type,
|
|
|
|
None => return,
|
|
|
|
};
|
|
|
|
|
|
|
|
let ty = match idl_type.to_syn_type(TypePosition::Return) {
|
|
|
|
Some(ty) => ty,
|
|
|
|
None => {
|
|
|
|
warn!(
|
|
|
|
"Cannot convert const type to syn type: {:?} in {:?} on {:?}",
|
|
|
|
idl_type,
|
|
|
|
member,
|
|
|
|
self_name
|
|
|
|
);
|
|
|
|
return
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
program.consts.push(backend::ast::Const {
|
|
|
|
vis: public(),
|
|
|
|
name: rust_ident(member.identifier.0.to_shouty_snake_case().as_str()),
|
|
|
|
class: Some(rust_ident(camel_case_ident(&self_name).as_str())),
|
|
|
|
ty,
|
|
|
|
value: webidl_const_v_to_backend_const_v(&member.const_value),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn append_interface(
|
|
|
|
&self,
|
|
|
|
program: &mut backend::ast::Program,
|
|
|
|
name: &'src str,
|
2018-08-29 17:33:35 -07:00
|
|
|
data: &InterfaceData<'src>,
|
2018-08-28 16:32:31 -07:00
|
|
|
) {
|
|
|
|
let doc_comment = Some(format!(
|
|
|
|
"The `{}` object\n\n{}",
|
|
|
|
name,
|
|
|
|
mdn_doc(name, None),
|
|
|
|
));
|
|
|
|
|
|
|
|
program.imports.push(backend::ast::Import {
|
|
|
|
module: None,
|
|
|
|
js_namespace: None,
|
|
|
|
kind: backend::ast::ImportKind::Type(backend::ast::ImportType {
|
|
|
|
vis: public(),
|
|
|
|
rust_name: rust_ident(camel_case_ident(name).as_str()),
|
|
|
|
js_name: name.to_string(),
|
|
|
|
attrs: Vec::new(),
|
|
|
|
doc_comment,
|
|
|
|
instanceof_shim: format!("__widl_instanceof_{}", name),
|
|
|
|
extends: self.all_superclasses(name)
|
|
|
|
.map(|name| Ident::new(&name, Span::call_site()))
|
|
|
|
.collect(),
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
|
2018-08-29 18:27:49 -07:00
|
|
|
// for (ctor_name, args) in data.constructors.iter() {
|
|
|
|
// self.append_constructor(program, name, ctor_name, args);
|
|
|
|
// }
|
2018-08-29 17:33:35 -07:00
|
|
|
for (id, op_data) in data.operations2.iter() {
|
2018-08-29 18:27:49 -07:00
|
|
|
// if let OperationId::Constructor = id {
|
|
|
|
// continue // TODO
|
|
|
|
// }
|
2018-08-29 17:33:35 -07:00
|
|
|
self.member_operation2(program, name, data, id, op_data);
|
2018-08-28 16:32:31 -07:00
|
|
|
}
|
|
|
|
for member in data.consts.iter() {
|
|
|
|
self.append_const(program, name, member);
|
|
|
|
}
|
|
|
|
for member in data.attributes.iter() {
|
|
|
|
self.member_attribute(
|
|
|
|
program,
|
|
|
|
name,
|
|
|
|
&member.attributes,
|
|
|
|
member.modifier,
|
|
|
|
member.readonly.is_some(),
|
|
|
|
&member.type_,
|
|
|
|
member.identifier.0,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-08-29 17:33:35 -07:00
|
|
|
for mixin_data in self.all_mixins(name) {
|
|
|
|
for (id, op_data) in mixin_data.operations2.iter() {
|
|
|
|
self.member_operation2(program, name, data, id, op_data);
|
2018-08-28 16:32:31 -07:00
|
|
|
}
|
2018-08-29 17:33:35 -07:00
|
|
|
for member in &mixin_data.consts {
|
2018-08-29 10:27:44 -07:00
|
|
|
self.append_const(program, name, member);
|
2018-08-28 16:32:31 -07:00
|
|
|
}
|
2018-08-29 17:33:35 -07:00
|
|
|
for member in &mixin_data.attributes {
|
2018-08-28 16:32:31 -07:00
|
|
|
self.member_attribute(
|
|
|
|
program,
|
2018-08-29 10:27:44 -07:00
|
|
|
name,
|
2018-08-28 16:32:31 -07:00
|
|
|
&member.attributes,
|
|
|
|
if let Some(s) = member.stringifier {
|
|
|
|
Some(weedle::interface::StringifierOrInheritOrStatic::Stringifier(s))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
|
|
|
member.readonly.is_some(),
|
|
|
|
&member.type_,
|
|
|
|
member.identifier.0,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-29 18:27:49 -07:00
|
|
|
// fn append_constructor(
|
|
|
|
// &self,
|
|
|
|
// program: &mut backend::ast::Program,
|
|
|
|
// iface_name: &'src str,
|
|
|
|
// ctor_name: &'src str,
|
|
|
|
// args: &[Argument<'src>],
|
|
|
|
// ) {
|
|
|
|
// let (overloaded, same_argument_names) = self.get_operation_overloading(
|
|
|
|
// args,
|
|
|
|
// &::first_pass::OperationId::Constructor,
|
|
|
|
// iface_name,
|
|
|
|
// false,
|
|
|
|
// );
|
|
|
|
//
|
|
|
|
// let self_ty = ident_ty(rust_ident(camel_case_ident(iface_name).as_str()));
|
|
|
|
//
|
|
|
|
// let kind = backend::ast::ImportFunctionKind::Method {
|
|
|
|
// class: ctor_name.to_string(),
|
|
|
|
// ty: self_ty.clone(),
|
|
|
|
// kind: backend::ast::MethodKind::Constructor,
|
|
|
|
// };
|
|
|
|
//
|
|
|
|
// let structural = false;
|
|
|
|
//
|
|
|
|
// // Constructors aren't annotated with `[Throws]` extended attributes
|
|
|
|
// // (how could they be, since they themselves are extended
|
|
|
|
// // attributes?) so we must conservatively assume that they can
|
|
|
|
// // always throw.
|
|
|
|
// //
|
|
|
|
// // From https://heycam.github.io/webidl/#Constructor (emphasis
|
|
|
|
// // mine):
|
|
|
|
// //
|
|
|
|
// // > The prose definition of a constructor must either return an IDL
|
|
|
|
// // > value of a type corresponding to the interface the
|
|
|
|
// // > `[Constructor]` extended attribute appears on, **or throw an
|
|
|
|
// // > exception**.
|
|
|
|
// let throws = true;
|
|
|
|
//
|
|
|
|
// for import_function in self.create_function(
|
|
|
|
// "new",
|
|
|
|
// overloaded,
|
|
|
|
// same_argument_names,
|
|
|
|
// &match self.convert_arguments(args) {
|
|
|
|
// Some(arguments) => arguments,
|
|
|
|
// None => return,
|
|
|
|
// },
|
|
|
|
// IdlType::Interface(iface_name),
|
|
|
|
// kind,
|
|
|
|
// structural,
|
|
|
|
// throws,
|
|
|
|
// None,
|
|
|
|
// ) {
|
|
|
|
// program.imports.push(wrap_import_function(import_function));
|
|
|
|
// }
|
|
|
|
// }
|
2018-08-28 16:32:31 -07:00
|
|
|
|
|
|
|
fn member_attribute(
|
|
|
|
&self,
|
|
|
|
program: &mut backend::ast::Program,
|
|
|
|
self_name: &'src str,
|
|
|
|
attrs: &'src Option<ExtendedAttributeList>,
|
|
|
|
modifier: Option<weedle::interface::StringifierOrInheritOrStatic>,
|
|
|
|
readonly: bool,
|
|
|
|
type_: &'src weedle::types::AttributedType<'src>,
|
|
|
|
identifier: &'src str,
|
|
|
|
) {
|
|
|
|
use weedle::interface::StringifierOrInheritOrStatic::*;
|
|
|
|
|
|
|
|
let is_static = match modifier {
|
|
|
|
Some(Stringifier(_)) => unimplemented!(), // filtered out earlier
|
|
|
|
Some(Inherit(_)) => false,
|
|
|
|
Some(Static(_)) => true,
|
|
|
|
None => false,
|
|
|
|
};
|
|
|
|
|
|
|
|
let is_structural = util::is_structural(attrs);
|
|
|
|
let throws = util::throws(attrs);
|
|
|
|
let global = self
|
|
|
|
.interfaces
|
|
|
|
.get(self_name)
|
|
|
|
.map(|interface_data| interface_data.global)
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
|
|
for import_function in self.create_getter(
|
|
|
|
identifier,
|
|
|
|
&type_.type_,
|
|
|
|
self_name,
|
|
|
|
is_static,
|
|
|
|
is_structural,
|
|
|
|
throws,
|
|
|
|
global,
|
|
|
|
) {
|
|
|
|
program.imports.push(wrap_import_function(import_function));
|
|
|
|
}
|
|
|
|
|
|
|
|
if !readonly {
|
|
|
|
for import_function in self.create_setter(
|
|
|
|
identifier,
|
|
|
|
type_.type_.clone(),
|
|
|
|
self_name,
|
|
|
|
is_static,
|
|
|
|
is_structural,
|
|
|
|
throws,
|
|
|
|
global,
|
|
|
|
) {
|
|
|
|
program.imports.push(wrap_import_function(import_function));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-29 17:33:35 -07:00
|
|
|
fn member_operation2(
|
2018-08-28 16:32:31 -07:00
|
|
|
&self,
|
|
|
|
program: &mut backend::ast::Program,
|
2018-08-29 17:33:35 -07:00
|
|
|
self_name: &str,
|
|
|
|
data: &InterfaceData<'src>,
|
|
|
|
id: &OperationId<'src>,
|
|
|
|
op_data: &OperationData2<'src>,
|
2018-08-28 16:32:31 -07:00
|
|
|
) {
|
2018-08-29 18:27:49 -07:00
|
|
|
let import_function_kind = |opkind| {
|
|
|
|
self.import_function_kind(self_name, data.global, op_data.is_static, opkind)
|
2018-08-29 17:33:35 -07:00
|
|
|
};
|
2018-08-29 18:27:49 -07:00
|
|
|
let kind = match id {
|
|
|
|
OperationId::Constructor(ctor_name) => {
|
|
|
|
let self_ty = ident_ty(rust_ident(&camel_case_ident(self_name)));
|
|
|
|
backend::ast::ImportFunctionKind::Method {
|
|
|
|
class: ctor_name.0.to_string(),
|
|
|
|
ty: self_ty.clone(),
|
|
|
|
kind: backend::ast::MethodKind::Constructor,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
OperationId::Operation(_) => {
|
|
|
|
import_function_kind(backend::ast::OperationKind::Regular)
|
|
|
|
}
|
|
|
|
OperationId::IndexingGetter => {
|
|
|
|
import_function_kind(backend::ast::OperationKind::IndexingGetter)
|
2018-08-28 16:32:31 -07:00
|
|
|
}
|
2018-08-29 18:27:49 -07:00
|
|
|
OperationId::IndexingSetter => {
|
|
|
|
import_function_kind(backend::ast::OperationKind::IndexingSetter)
|
|
|
|
}
|
|
|
|
OperationId::IndexingDeleter => {
|
|
|
|
import_function_kind(backend::ast::OperationKind::IndexingDeleter)
|
2018-08-29 17:33:35 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
for method in self.create_imports(kind, id, op_data) {
|
|
|
|
program.imports.push(wrap_import_function(method));
|
2018-08-28 16:32:31 -07:00
|
|
|
}
|
|
|
|
}
|
2018-08-14 10:16:18 -07:00
|
|
|
}
|