diff --git a/crates/web-sys/build.rs b/crates/web-sys/build.rs index 0496a979..8dbb232c 100644 --- a/crates/web-sys/build.rs +++ b/crates/web-sys/build.rs @@ -1,4 +1,5 @@ extern crate env_logger; +#[macro_use] extern crate failure; extern crate wasm_bindgen_webidl; extern crate sourcefile; @@ -8,9 +9,8 @@ use sourcefile::SourceFile; use std::env; use std::ffi::OsStr; use std::fs; -use std::io::Write; use std::path; -use std::process; +use std::process::{self, Command}; fn main() { if let Err(e) = try_main() { @@ -32,12 +32,14 @@ fn try_main() -> Result<(), failure::Error> { let mut source = SourceFile::default(); for entry in entries { let entry = entry.context("getting webidls/enabled/*.webidl entry")?; - if entry.path().extension() == Some(OsStr::new("webidl")) { - println!("cargo:rerun-if-changed={}", entry.path().display()); - source = source.add_file(entry.path()) - .with_context(|_| format!("reading contents of file \"{}\"", - entry.path().display()))?; + let path = entry.path(); + if path.extension() != Some(OsStr::new("webidl")) { + continue } + println!("cargo:rerun-if-changed={}", path.display()); + source = source.add_file(&path) + .with_context(|_| format!("reading contents of file \"{}\"", + path.display()))?; } let bindings = match wasm_bindgen_webidl::compile(&source.contents) { @@ -60,17 +62,19 @@ fn try_main() -> Result<(), failure::Error> { let out_dir = env::var("OUT_DIR").context("reading OUT_DIR environment variable")?; let out_file_path = path::Path::new(&out_dir).join("bindings.rs"); - let mut out_file = fs::File::create(&out_file_path) - .context("creating output bindings file")?; - out_file - .write_all(bindings.as_bytes()) + fs::write(&out_file_path, bindings) .context("writing bindings to output file")?; // run rustfmt on the generated file - really handy for debugging - //if ! process::Command::new("rustfmt").arg(&out_file_path).status() - // .context("running rustfmt")?.success() { - // return Err(format_err!("rustfmt failed to format {}", out_file_path.display())); - //} + if env::var("WEBIDL_RUSTFMT_BINDINGS").is_ok() { + let status = Command::new("rustfmt") + .arg(&out_file_path) + .status() + .context("running rustfmt")?; + if !status.success() { + bail!("rustfmt failed: {}", status) + } + } Ok(()) } diff --git a/crates/webidl/Cargo.toml b/crates/webidl/Cargo.toml index e953891e..471b4c73 100644 --- a/crates/webidl/Cargo.toml +++ b/crates/webidl/Cargo.toml @@ -20,4 +20,4 @@ proc-macro2 = "0.4.8" quote = '0.6' syn = { version = '0.14', features = ['full'] } wasm-bindgen-backend = { version = "=0.2.15", path = "../backend" } -webidl = "0.7.0" +weedle = "0.6" diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 6242ed09..5b9a39be 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -7,66 +7,58 @@ //! Only `interface`s, `dictionary`s, `enum`s and `mixin`s can //! be partial. -use std::{ - collections::{BTreeMap, BTreeSet}, mem, -}; +use std::collections::{BTreeMap, BTreeSet}; -use webidl; +use weedle::argument::Argument; +use weedle::attribute::ExtendedAttribute; +use weedle::interface::StringifierOrStatic; +use weedle::mixin::MixinMembers; +use weedle; use super::Result; +use util; /// Collection of constructs that may use partial. #[derive(Default)] -pub(crate) struct FirstPassRecord<'a> { - pub(crate) interfaces: BTreeMap, - pub(crate) dictionaries: BTreeSet, - pub(crate) enums: BTreeSet, +pub(crate) struct FirstPassRecord<'src> { + pub(crate) interfaces: BTreeMap<&'src str, InterfaceData<'src>>, + pub(crate) dictionaries: BTreeSet<&'src str>, + pub(crate) enums: BTreeSet<&'src str>, /// The mixins, mapping their name to the webidl ast node for the mixin. - pub(crate) mixins: BTreeMap>, - pub(crate) typedefs: BTreeMap, + pub(crate) mixins: BTreeMap<&'src str, Vec<&'src MixinMembers<'src>>>, + pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>, } /// We need to collect interface data during the first pass, to be used later. #[derive(Default)] -pub(crate) struct InterfaceData { +pub(crate) struct InterfaceData<'src> { /// Whether only partial interfaces were encountered pub(crate) partial: bool, - pub(crate) operations: BTreeMap, pub(crate) global: bool, + pub(crate) operations: BTreeMap, OperationData<'src>>, } #[derive(PartialEq, Eq, PartialOrd, Ord)] -pub(crate) enum OperationId { +pub(crate) enum OperationId<'src> { Constructor, - Operation(Option) + Operation(Option<&'src str>) } #[derive(Default)] -pub(crate) struct OperationData { +pub(crate) struct OperationData<'src> { pub(crate) overloaded: bool, /// Map from argument names to whether they are the same for multiple overloads - pub(crate) argument_names_same: BTreeMap, bool>, -} - -/// We need to collect mixin data during the first pass, to be used later. -#[derive(Default)] -pub(crate) struct MixinData<'a> { - /// The non partial mixin, if present. If there is more than one, we are - /// parsing is a malformed WebIDL file, but the parser will recover by - /// using the last parsed mixin. - pub(crate) non_partial: Option<&'a webidl::ast::NonPartialMixin>, - /// 0 or more partial mixins. - pub(crate) partials: Vec<&'a webidl::ast::PartialMixin>, + pub(crate) argument_names_same: BTreeMap, bool>, } /// Implemented on an AST node to populate the `FirstPassRecord` struct. -pub(crate) trait FirstPass { +pub(crate) trait FirstPass<'src, Ctx> { /// Populate `record` with any constructs in `self`. - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, ctx: Ctx) -> Result<()>; + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, ctx: Ctx) -> Result<()>; } -impl FirstPass<()> for [webidl::ast::Definition] { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { +impl<'src> FirstPass<'src, ()> for [weedle::Definition<'src>] { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { for def in self { def.first_pass(record, ())?; } @@ -75,15 +67,17 @@ impl FirstPass<()> for [webidl::ast::Definition] { } } -impl FirstPass<()> for webidl::ast::Definition { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - use webidl::ast::Definition::*; +impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + use weedle::Definition::*; match self { Dictionary(dictionary) => dictionary.first_pass(record, ()), Enum(enum_) => enum_.first_pass(record, ()), Interface(interface) => interface.first_pass(record, ()), - Mixin(mixin) => mixin.first_pass(record, ()), + PartialInterface(interface) => interface.first_pass(record, ()), + InterfaceMixin(mixin) => mixin.first_pass(record, ()), + PartialInterfaceMixin(mixin) => mixin.first_pass(record, ()), Typedef(typedef) => typedef.first_pass(record, ()), _ => { // Other definitions aren't currently used in the first pass @@ -93,46 +87,38 @@ impl FirstPass<()> for webidl::ast::Definition { } } -impl FirstPass<()> for webidl::ast::Dictionary { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - use webidl::ast::Dictionary::*; - - match self { - NonPartial(dictionary) => dictionary.first_pass(record, ()), - _ => { - // Other dictionaries aren't currently used in the first pass - Ok(()) - } +impl<'src> FirstPass<'src, ()> for weedle::DictionaryDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if !record.dictionaries.insert(self.identifier.0) { + warn!("encountered multiple dictionary declarations of {}", self.identifier.0); } + Ok(()) } } -impl FirstPass<()> for webidl::ast::NonPartialDictionary { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - if record.dictionaries.insert(self.name.clone()) { - warn!("Encountered multiple declarations of {}", self.name); +impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if !record.enums.insert(self.identifier.0) { + warn!("Encountered multiple enum declarations of {}", self.identifier.0); } Ok(()) } } -impl FirstPass<()> for webidl::ast::Enum { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - if record.enums.insert(self.name.clone()) { - warn!("Encountered multiple declarations of {}", self.name); - } - - Ok(()) - } -} - -fn first_pass_operation<'a>( - record: &mut FirstPassRecord<'a>, - self_name: &str, - id: OperationId, - arguments: &[webidl::ast::Argument], +fn first_pass_operation<'src>( + record: &mut FirstPassRecord<'src>, + self_name: &'src str, + id: OperationId<'src>, + arguments: &[Argument<'src>], ) -> Result<()> { + let mut names = Vec::with_capacity(arguments.len()); + for argument in arguments { + match argument { + Argument::Single(arg) => names.push(arg.identifier.0), + Argument::Variadic(_) => return Ok(()), + } + } record .interfaces .get_mut(self_name) @@ -140,77 +126,48 @@ fn first_pass_operation<'a>( .operations .entry(id) .and_modify(|operation_data| operation_data.overloaded = true) - .or_insert_with(|| - OperationData { - overloaded: false, - argument_names_same: Default::default(), - } - ) + .or_insert_with(Default::default) .argument_names_same - .entry(arguments.iter().map(|argument| argument.name.clone()).collect()) + .entry(names) .and_modify(|same_argument_names| *same_argument_names = true) .or_insert(false); Ok(()) } -impl FirstPass<()> for webidl::ast::Interface { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - use webidl::ast::Interface::*; - - match self { - Partial(interface) => interface.first_pass(record, ()), - NonPartial(interface) => interface.first_pass(record, ()), - // TODO - Callback(..) => { - warn!("Unsupported WebIDL interface: {:?}", self); - Ok(()) - } +impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + { + let interface = record + .interfaces + .entry(self.identifier.0) + .or_insert_with(Default::default); + interface.partial = false; } - } -} -impl FirstPass<()> for webidl::ast::NonPartialInterface { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - record - .interfaces - .entry(self.name.clone()) - .and_modify(|interface_data| { - if interface_data.partial { - interface_data.partial = false; - } else { - warn!("Encountered multiple declarations of {}", self.name); - } - }) - .or_insert_with(|| - InterfaceData { - partial: false, - operations: Default::default(), - global: false, - }, - ); - - if ::util::is_chrome_only(&self.extended_attributes) { + if util::is_chrome_only(&self.attributes) { return Ok(()) } - for extended_attribute in &self.extended_attributes { - extended_attribute.first_pass(record, &self.name)?; + if let Some(attrs) = &self.attributes { + for attr in &attrs.body.list { + attr.first_pass(record, self.identifier.0)?; + } } - for member in &self.members { - member.first_pass(record, &self.name)?; + for member in &self.members.body { + member.first_pass(record, self.identifier.0)?; } Ok(()) } } -impl FirstPass<()> for webidl::ast::PartialInterface { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { +impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { record .interfaces - .entry(self.name.clone()) + .entry(self.identifier.0) .or_insert_with(|| InterfaceData { partial: true, @@ -219,168 +176,120 @@ impl FirstPass<()> for webidl::ast::PartialInterface { }, ); - if ::util::is_chrome_only(&self.extended_attributes) { + if util::is_chrome_only(&self.attributes) { return Ok(()) } - for member in &self.members { - member.first_pass(record, &self.name)?; + for member in &self.members.body { + member.first_pass(record, self.identifier.0)?; } Ok(()) } } -impl<'b> FirstPass<&'b str> for webidl::ast::ExtendedAttribute { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> { +impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { match self { - webidl::ast::ExtendedAttribute::ArgumentList( - webidl::ast::ArgumentListExtendedAttribute { arguments, name }, - ) - if name == "Constructor" => - { - first_pass_operation( - record, - self_name, - OperationId::Constructor, - &arguments, - ) - } - webidl::ast::ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) - if name == "Constructor" => - { - first_pass_operation( - record, - self_name, - OperationId::Constructor, - &[], - ) - } - webidl::ast::ExtendedAttribute::NamedArgumentList( - webidl::ast::NamedArgumentListExtendedAttribute { - lhs_name, - rhs_arguments, - .. - }, - ) - if lhs_name == "NamedConstructor" => - { - first_pass_operation( - record, - self_name, - OperationId::Constructor, - &rhs_arguments, - ) - }, - webidl::ast::ExtendedAttribute::Identifier( - webidl::ast::IdentifierExtendedAttribute { lhs, .. } - ) - | webidl::ast::ExtendedAttribute::IdentifierList( - webidl::ast::IdentifierListExtendedAttribute { lhs, .. } - ) - if lhs == "Global" => - { - record.interfaces.get_mut(self_name).unwrap().global = true; - Ok(()) - } + ExtendedAttribute::ArgList(list) if list.identifier.0 == "Constructor" => { + first_pass_operation( + record, + self_name, + OperationId::Constructor, + &list.args.body.list, + ) + } + ExtendedAttribute::NoArgs(name) if (name.0).0 == "Constructor" => { + first_pass_operation( + record, + self_name, + OperationId::Constructor, + &[], + ) + } + ExtendedAttribute::NamedArgList(list) + if list.lhs_identifier.0 == "NamedConstructor" => + { + first_pass_operation( + record, + self_name, + OperationId::Constructor, + &list.args.body.list, + ) + } + ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Global" => { + record.interfaces.get_mut(self_name).unwrap().global = true; + Ok(()) + } + ExtendedAttribute::IdentList(id) if id.identifier.0 == "Global" => { + record.interfaces.get_mut(self_name).unwrap().global = true; + Ok(()) + } _ => Ok(()) } } } -impl<'b> FirstPass<&'b str> for webidl::ast::InterfaceMember { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> { +impl<'src> FirstPass<'src, &'src str> for weedle::interface::InterfaceMember<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { match self { - webidl::ast::InterfaceMember::Operation(op) => op.first_pass(record, self_name), + weedle::interface::InterfaceMember::Operation(op) => { + op.first_pass(record, self_name) + } _ => Ok(()), } } } -impl<'b> FirstPass<&'b str> for webidl::ast::Operation { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> { - match self { - webidl::ast::Operation::Regular(op) => op.first_pass(record, self_name), - webidl::ast::Operation::Static(op) => op.first_pass(record, self_name), - // TODO - webidl::ast::Operation::Special(_) | webidl::ast::Operation::Stringifier(_) => { - warn!("Unsupported WebIDL operation: {:?}", self); - Ok(()) - } +impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceMember<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { + if self.specials.len() > 0 { + warn!("Unsupported webidl operation {:?}", self); + return Ok(()) + } + if let Some(StringifierOrStatic::Stringifier(_)) = self.modifier { + warn!("Unsupported webidl operation {:?}", self); + return Ok(()) } - } -} - -impl<'b> FirstPass<&'b str> for webidl::ast::RegularOperation { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> { first_pass_operation( record, self_name, - OperationId::Operation(self.name.clone()), - &self.arguments, + OperationId::Operation(self.identifier.map(|s| s.0)), + &self.args.body.list, ) } } -impl<'b> FirstPass<&'b str> for webidl::ast::StaticOperation { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> { - first_pass_operation( - record, - self_name, - OperationId::Operation(self.name.clone()), - &self.arguments, - ) - } -} - -impl FirstPass<()> for webidl::ast::Mixin { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - use webidl::ast::Mixin::*; - - match self { - NonPartial(mixin) => mixin.first_pass(record, ()), - Partial(mixin) => mixin.first_pass(record, ()), - } - } -} - -impl FirstPass<()> for webidl::ast::NonPartialMixin { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - let entry = record +impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src>{ + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + record .mixins - .entry(self.name.clone()) - .or_insert_with(Default::default); - if mem::replace(&mut entry.non_partial, Some(self)).is_some() { - warn!( - "Encounterd multiple declarations of {}, using last encountered", - self.name - ); - } - + .entry(self.identifier.0) + .or_insert_with(Default::default) + .push(&self.members.body); Ok(()) } } -impl FirstPass<()> for webidl::ast::PartialMixin { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - let entry = record +impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + record .mixins - .entry(self.name.clone()) - .or_insert_with(Default::default); - entry.partials.push(self); - + .entry(self.identifier.0) + .or_insert_with(Default::default) + .push(&self.members.body); Ok(()) } } -impl FirstPass<()> for webidl::ast::Typedef { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - if ::util::is_chrome_only(&self.extended_attributes) { +impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if util::is_chrome_only(&self.attributes) { return Ok(()); } - if record.typedefs.insert(self.name.clone(), *self.type_.clone()).is_some() { - warn!("Encountered multiple declarations of {}", self.name); + if record.typedefs.insert(self.identifier.0, &self.type_.type_).is_some() { + warn!("Encountered multiple declarations of {}", self.identifier.0); } Ok(()) diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index ec41686d..6c3fb030 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -9,6 +9,7 @@ emitted for the types and methods described in the WebIDL. #![deny(missing_debug_implementations)] #![doc(html_root_url = "https://docs.rs/wasm-bindgen-webidl/0.2")] +#[macro_use] extern crate failure; #[macro_use] extern crate failure_derive; @@ -21,7 +22,7 @@ extern crate quote; #[macro_use] extern crate syn; extern crate wasm_bindgen_backend as backend; -extern crate webidl; +extern crate weedle; mod first_pass; mod util; @@ -36,11 +37,14 @@ use std::path::Path; use backend::TryToTokens; use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports}; use backend::util::{ident_ty, rust_ident, wrap_import_function}; -use failure::{ResultExt, Fail}; +use failure::ResultExt; use heck::{ShoutySnakeCase}; +use weedle::argument::Argument; +use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList}; use first_pass::{FirstPass, FirstPassRecord}; -use util::{ApplyTypedefs, public, webidl_const_ty_to_syn_ty, webidl_const_v_to_backend_const_v, camel_case_ident, mdn_doc}; +use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc}; +use util::ToSynType; pub use error::{Error, ErrorKind, Result}; @@ -55,22 +59,31 @@ fn parse_file(webidl_path: &Path) -> Result { /// Parse a string of WebIDL source text into a wasm-bindgen AST. fn parse(webidl_source: &str) -> Result { - let definitions = match webidl::parse_string(webidl_source) { + let definitions = match weedle::parse(webidl_source) { Ok(def) => def, Err(e) => { - let kind = match &e { - webidl::ParseError::InvalidToken { location } => { - ErrorKind::ParsingWebIDLSourcePos(*location) + return Err(match &e { + weedle::Err::Incomplete(needed) => { + format_err!("needed {:?} more bytes", needed) + .context(ErrorKind::ParsingWebIDLSource).into() } - webidl::ParseError::UnrecognizedToken { token: Some((start, ..)), .. } => { - ErrorKind::ParsingWebIDLSourcePos(*start) + 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() } - webidl::ParseError::ExtraToken { token: (start, ..) } => { - ErrorKind::ParsingWebIDLSourcePos(*start) - }, - _ => ErrorKind::ParsingWebIDLSource - }; - return Err(e.context(kind).into()); + // webidl::ParseError::UnrecognizedToken { token: Some((start, ..)), .. } => { + // ErrorKind::ParsingWebIDLSourcePos(*start) + // } + // webidl::ParseError::ExtraToken { token: (start, ..) } => { + // ErrorKind::ParsingWebIDLSourcePos(*start) + // }, + // _ => ErrorKind::ParsingWebIDLSource + }); } }; @@ -119,21 +132,21 @@ fn compile_ast(mut ast: backend::ast::Program) -> String { } /// The main trait for parsing WebIDL AST into wasm-bindgen AST. -trait WebidlParse { +trait WebidlParse<'src, Ctx> { /// Parse `self` into wasm-bindgen AST, and insert it into `program`. fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, context: Ctx, ) -> Result<()>; } -impl WebidlParse<()> for [webidl::ast::Definition] { +impl<'src> WebidlParse<'src, ()> for [weedle::Definition<'src>] { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, (): (), ) -> Result<()> { for def in self { @@ -143,104 +156,85 @@ impl WebidlParse<()> for [webidl::ast::Definition] { } } -impl WebidlParse<()> for webidl::ast::Definition { +impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, (): (), ) -> Result<()> { match self { - webidl::ast::Definition::Enum(enumeration) => { + weedle::Definition::Enum(enumeration) => { enumeration.webidl_parse(program, first_pass, ())? } - webidl::ast::Definition::Includes(includes) => { + weedle::Definition::IncludesStatement(includes) => { includes.webidl_parse(program, first_pass, ())? } - webidl::ast::Definition::Interface(interface) => { + weedle::Definition::Interface(interface) => { interface.webidl_parse(program, first_pass, ())? } - // TODO - webidl::ast::Definition::Callback(..) - | webidl::ast::Definition::Dictionary(..) - | webidl::ast::Definition::Implements(..) - | webidl::ast::Definition::Namespace(..) => { - warn!("Unsupported WebIDL definition: {:?}", self) + weedle::Definition::PartialInterface(interface) => { + interface.webidl_parse(program, first_pass, ())? } - webidl::ast::Definition::Mixin(_) - | webidl::ast::Definition::Typedef(_) => { + weedle::Definition::Typedef(_) | + weedle::Definition::InterfaceMixin(_) | + weedle::Definition::PartialInterfaceMixin(_) => { // handled in the first pass } - } - Ok(()) - } -} - -impl WebidlParse<()> for webidl::ast::Includes { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - (): (), - ) -> Result<()> { - match first_pass.mixins.get(&self.includee) { - Some(mixin) => { - if let Some(non_partial) = mixin.non_partial { - for member in &non_partial.members { - member.webidl_parse(program, first_pass, &self.includer)?; - } - } - for partial in &mixin.partials { - for member in &partial.members { - member.webidl_parse(program, first_pass, &self.includer)?; - } - } - } - None => warn!("Tried to include missing mixin {}", self.includee), - } - Ok(()) - } -} - -impl WebidlParse<()> for webidl::ast::Interface { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - (): (), - ) -> Result<()> { - match self { - webidl::ast::Interface::NonPartial(interface) => { - interface.webidl_parse(program, first_pass, ()) - } - webidl::ast::Interface::Partial(interface) => { - interface.webidl_parse(program, first_pass, ()) - } // TODO - webidl::ast::Interface::Callback(..) => { - warn!("Unsupported WebIDL interface: {:?}", self); - Ok(()) + weedle::Definition::Callback(..) + | weedle::Definition::CallbackInterface(..) + | weedle::Definition::Dictionary(..) + | weedle::Definition::PartialDictionary(..) + | weedle::Definition::Implements(..) + | weedle::Definition::Namespace(..) + | weedle::Definition::PartialNamespace(..) => { + warn!("Unsupported WebIDL definition: {:?}", self) } } + Ok(()) } } -impl WebidlParse<()> for webidl::ast::NonPartialInterface { +impl<'src> WebidlParse<'src, ()> for weedle::IncludesStatementDefinition<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, (): (), ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { + match first_pass.mixins.get(self.rhs_identifier.0) { + Some(member_lists) => { + for member in member_lists.iter().flat_map(|list| list.iter()) { + member.webidl_parse(program, first_pass, self.lhs_identifier.0)?; + } + } + None => warn!("Tried to include missing mixin {}", self.rhs_identifier.0), + } + Ok(()) + } +} + +impl<'src> WebidlParse<'src, ()> for weedle::InterfaceDefinition<'src> { + fn webidl_parse( + &'src self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + (): (), + ) -> Result<()> { + if util::is_chrome_only(&self.attributes) { return Ok(()); } - if util::is_no_interface_object(&self.extended_attributes) { + if util::is_no_interface_object(&self.attributes) { return Ok(()); } - let doc_comment = Some(format!("The `{}` object\n\n{}", &self.name, mdn_doc(&self.name, None))); + let doc_comment = Some(format!( + "The `{}` object\n\n{}", + self.identifier.0, + mdn_doc(self.identifier.0, None), + )); program.imports.push(backend::ast::Import { module: None, @@ -248,70 +242,67 @@ impl WebidlParse<()> for webidl::ast::NonPartialInterface { js_namespace: None, kind: backend::ast::ImportKind::Type(backend::ast::ImportType { vis: public(), - name: rust_ident(camel_case_ident(&self.name).as_str()), + name: rust_ident(camel_case_ident(self.identifier.0).as_str()), attrs: Vec::new(), doc_comment, }), }); - for extended_attribute in &self.extended_attributes { - extended_attribute.webidl_parse(program, first_pass, self)?; + if let Some(attrs) = &self.attributes { + for attr in &attrs.body.list { + attr.webidl_parse(program, first_pass, self)?; + } } - for member in &self.members { - member.webidl_parse(program, first_pass, &self.name)?; + for member in &self.members.body { + member.webidl_parse(program, first_pass, self.identifier.0)?; } Ok(()) } } -impl WebidlParse<()> for webidl::ast::PartialInterface { +impl<'src> WebidlParse<'src, ()> for weedle::PartialInterfaceDefinition<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, (): (), ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { + if util::is_chrome_only(&self.attributes) { return Ok(()); } - if !first_pass.interfaces.contains_key(&self.name) { + if !first_pass.interfaces.contains_key(self.identifier.0) { warn!( "Partial interface {} missing non-partial interface", - self.name + self.identifier.0 ); } - for member in &self.members { - member.webidl_parse(program, first_pass, &self.name)?; + for member in &self.members.body { + member.webidl_parse(program, first_pass, self.identifier.0)?; } Ok(()) } } -impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::ExtendedAttribute { +impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for ExtendedAttribute<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - interface: &'a webidl::ast::NonPartialInterface, + first_pass: &FirstPassRecord<'src>, + interface: &'src weedle::InterfaceDefinition<'src>, ) -> Result<()> { - let mut add_constructor = |arguments: &[webidl::ast::Argument], class: &str| { - let arguments = &arguments - .iter() - .map(|argument| argument.apply_typedefs(first_pass)) - .collect::>(); - + let mut add_constructor = |arguments: &[Argument], class: &str| { let (overloaded, same_argument_names) = first_pass.get_operation_overloading( arguments, ::first_pass::OperationId::Constructor, - &interface.name, + interface.identifier.0, ); - let self_ty = ident_ty(rust_ident(camel_case_ident(&interface.name).as_str())); + let self_ty = ident_ty(rust_ident(camel_case_ident(interface.identifier.0).as_str())); let kind = backend::ast::ImportFunctionKind::Method { class: class.to_string(), @@ -340,9 +331,7 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte "new", overloaded, same_argument_names, - arguments - .iter() - .map(|arg| (&*arg.name, &*arg.type_, arg.variadic)), + arguments, Some(self_ty), kind, structural, @@ -354,34 +343,24 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte }; match self { - webidl::ast::ExtendedAttribute::ArgumentList( - webidl::ast::ArgumentListExtendedAttribute { arguments, name }, - ) - if name == "Constructor" => + ExtendedAttribute::ArgList(list) + if list.identifier.0 == "Constructor" => { - add_constructor(arguments, &interface.name) + add_constructor(&list.args.body.list, interface.identifier.0) } - webidl::ast::ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) - if name == "Constructor" => + ExtendedAttribute::NoArgs(other) if (other.0).0 == "Constructor" => { + add_constructor(&[], interface.identifier.0) + } + ExtendedAttribute::NamedArgList(list) + if list.lhs_identifier.0 == "NamedConstructor" => { - add_constructor(&[], &interface.name) + add_constructor(&list.args.body.list, list.rhs_identifier.0) } - webidl::ast::ExtendedAttribute::NamedArgumentList( - webidl::ast::NamedArgumentListExtendedAttribute { - lhs_name, - rhs_arguments, - rhs_name, - }, - ) - if lhs_name == "NamedConstructor" => - { - add_constructor(rhs_arguments, rhs_name) - } - webidl::ast::ExtendedAttribute::ArgumentList(_) - | webidl::ast::ExtendedAttribute::Identifier(_) - | webidl::ast::ExtendedAttribute::IdentifierList(_) - | webidl::ast::ExtendedAttribute::NamedArgumentList(_) - | webidl::ast::ExtendedAttribute::NoArguments(_) => { + ExtendedAttribute::ArgList(_) + | ExtendedAttribute::Ident(_) + | ExtendedAttribute::IdentList(_) + | ExtendedAttribute::NamedArgList(_) + | ExtendedAttribute::NoArgs(_) => { warn!("Unsupported WebIDL extended attribute: {:?}", self); } } @@ -390,29 +369,32 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte } } -impl<'a> WebidlParse<&'a str> for webidl::ast::InterfaceMember { +impl<'src> WebidlParse<'src, &'src str> for weedle::interface::InterfaceMember<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, ) -> Result<()> { + use weedle::interface::InterfaceMember::*; + match self { - webidl::ast::InterfaceMember::Attribute(attr) => { + Attribute(attr) => { attr.webidl_parse(program, first_pass, self_name) } - webidl::ast::InterfaceMember::Operation(op) => { + Operation(op) => { op.webidl_parse(program, first_pass, self_name) } - webidl::ast::InterfaceMember::Const(cnst) => { + Const(cnst) => { cnst.webidl_parse(program, first_pass, self_name) } - webidl::ast::InterfaceMember::Iterable(iterable) => { + Iterable(iterable) => { iterable.webidl_parse(program, first_pass, self_name) } // TODO - | webidl::ast::InterfaceMember::Maplike(_) - | webidl::ast::InterfaceMember::Setlike(_) => { + | Maplike(_) + | Stringifier(_) + | Setlike(_) => { warn!("Unsupported WebIDL interface member: {:?}", self); Ok(()) } @@ -420,128 +402,236 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::InterfaceMember { } } -impl<'a> WebidlParse<&'a str> for webidl::ast::MixinMember { +impl<'a, 'src> WebidlParse<'src, &'a str> for weedle::mixin::MixinMember<'src> { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, self_name: &'a str, ) -> Result<()> { match self { - webidl::ast::MixinMember::Attribute(attr) => { + weedle::mixin::MixinMember::Attribute(attr) => { attr.webidl_parse(program, first_pass, self_name) } - webidl::ast::MixinMember::Operation(op) => { + weedle::mixin::MixinMember::Operation(op) => { op.webidl_parse(program, first_pass, self_name) } // TODO - webidl::ast::MixinMember::Const(_) => { - warn!("Unsupported WebIDL interface member: {:?}", self); - Ok(()) - } - } - } -} -impl<'a> WebidlParse<&'a str> for webidl::ast::Attribute { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, - ) -> Result<()> { - match self { - webidl::ast::Attribute::Regular(attr) => { - attr.webidl_parse(program, first_pass, self_name) - } - webidl::ast::Attribute::Static(attr) => { - attr.webidl_parse(program, first_pass, self_name) - } - // TODO - webidl::ast::Attribute::Stringifier(_) => { - warn!("Unsupported WebIDL attribute: {:?}", self); + weedle::mixin::MixinMember::Stringifier(_) | + weedle::mixin::MixinMember::Const(_) => { + warn!("Unsupported WebIDL mixin member: {:?}", self); Ok(()) } } } } -impl<'a> WebidlParse<&'a str> for webidl::ast::Operation { +impl<'src> WebidlParse<'src, &'src str> for weedle::interface::AttributeInterfaceMember<'src> { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, ) -> Result<()> { - match self { - webidl::ast::Operation::Regular(op) => op.webidl_parse(program, first_pass, self_name), - webidl::ast::Operation::Static(op) => op.webidl_parse(program, first_pass, self_name), - // TODO - webidl::ast::Operation::Special(_) | webidl::ast::Operation::Stringifier(_) => { - warn!("Unsupported WebIDL operation: {:?}", self); - Ok(()) - } - } + member_attribute( + program, + first_pass, + self_name, + &self.attributes, + self.modifier, + self.readonly.is_some(), + &self.type_, + self.identifier.0, + ) } } -impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute { +impl<'src> WebidlParse<'src, &'src str> for weedle::mixin::AttributeMixinMember<'src> { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { - return Ok(()); + member_attribute( + program, + first_pass, + self_name, + &self.attributes, + if let Some(s) = self.stringifier { + Some(weedle::interface::StringifierOrInheritOrStatic::Stringifier(s)) + } else { + None + }, + self.readonly.is_some(), + &self.type_, + self.identifier.0, + ) + } +} + +fn member_attribute<'src>( + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, + attrs: &'src Option, + modifier: Option, + readonly: bool, + type_: &'src weedle::types::AttributedType<'src>, + identifier: &'src str, +) -> Result<()> { + use weedle::interface::StringifierOrInheritOrStatic::*; + + if util::is_chrome_only(attrs) { + return Ok(()); + } + + let statik = match modifier { + Some(Stringifier(_)) => { + warn!("Unsupported stringifier on type {:?}", (self_name, identifier)); + return Ok(()) } + Some(Inherit(_)) => false, + Some(Static(_)) => true, + None => false, + }; - let is_structural = util::is_structural(&self.extended_attributes); - let throws = util::throws(&self.extended_attributes); + if type_.attributes.is_some() { + warn!("Unsupported attributes on type {:?}", (self_name, identifier)); + return Ok(()) + } + let is_structural = util::is_structural(attrs); + let throws = util::throws(attrs); + + first_pass + .create_getter( + identifier, + &type_.type_, + self_name, + statik, + is_structural, + throws, + ) + .map(wrap_import_function) + .map(|import| program.imports.push(import)); + + if !readonly { first_pass - .create_getter( - &self.name, - &self.type_.apply_typedefs(first_pass), + .create_setter( + identifier, + type_.type_.clone(), self_name, - false, + statik, is_structural, throws, ) .map(wrap_import_function) .map(|import| program.imports.push(import)); + } - if !self.read_only { - first_pass - .create_setter( - &self.name, - &self.type_.apply_typedefs(first_pass), - self_name, - false, - is_structural, - throws, - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - } + Ok(()) +} - Ok(()) +impl<'src> WebidlParse<'src, &'src str> for weedle::interface::OperationInterfaceMember<'src> { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, + ) -> Result<()> { + member_operation( + program, + first_pass, + self_name, + &self.attributes, + self.modifier, + &self.specials, + &self.return_type, + &self.args.body.list, + &self.identifier, + ) } } -impl<'a> WebidlParse<&'a str> for webidl::ast::Iterable { +impl<'src> WebidlParse<'src, &'src str> for weedle::mixin::OperationMixinMember<'src> { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, + ) -> Result<()> { + member_operation( + program, + first_pass, + self_name, + &self.attributes, + None, + &[], + &self.return_type, + &self.args.body.list, + &self.identifier, + ) + } +} + +fn member_operation<'src>( + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, + attrs: &'src Option, + modifier: Option, + specials: &[weedle::interface::Special], + return_type: &'src weedle::types::ReturnType<'src>, + args: &'src [Argument], + identifier: &Option>, +) -> Result<()> { + use weedle::interface::StringifierOrStatic::*; + + if util::is_chrome_only(attrs) { + return Ok(()); + } + let statik = match modifier { + Some(Stringifier(_)) => { + warn!("Unsupported stringifier on type {:?}", (self_name, identifier)); + return Ok(()) + } + Some(Static(_)) => true, + None => false, + }; + if specials.len() > 0 { + warn!("Unsupported specials on type {:?}", (self_name, identifier)); + return Ok(()) + } + + first_pass + .create_basic_method( + args, + identifier.map(|s| s.0), + return_type, + self_name, + statik, + util::throws(attrs), + ) + .map(wrap_import_function) + .map(|import| program.imports.push(import)); + Ok(()) +} + +impl<'src> WebidlParse<'src, &'src str> for weedle::interface::IterableInterfaceMember<'src> { fn webidl_parse( &self, _program: &mut backend::ast::Program, - _first_pass: &FirstPassRecord<'_>, - _self_name: &'a str, + _first_pass: &FirstPassRecord<'src>, + _self_name: &'src str, ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { - return Ok(()); - } + // if util::is_chrome_only(&self.attributes) { + // return Ok(()); + // } /* TODO let throws = util::throws(&self.extended_attributes); - let return_value = webidl::ast::ReturnType::NonVoid(self.value_type.clone()); + let return_value = weedle::ReturnType::NonVoid(self.value_type.clone()); let args = []; first_pass .create_basic_method( @@ -572,142 +662,32 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::Iterable { } } -impl<'a> WebidlParse<&'a str> for webidl::ast::StaticAttribute { +impl<'src> WebidlParse<'src, ()> for weedle::EnumDefinition<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, - ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { - return Ok(()); - } - - let is_structural = util::is_structural(&self.extended_attributes); - let throws = util::throws(&self.extended_attributes); - - first_pass - .create_getter( - &self.name, - &self.type_.apply_typedefs(first_pass), - self_name, - true, - is_structural, - throws, - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - - if !self.read_only { - first_pass - .create_setter( - &self.name, - &self.type_.apply_typedefs(first_pass), - self_name, - true, - is_structural, - throws, - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - } - - Ok(()) - } -} - -impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, - ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { - return Ok(()); - } - - let throws = util::throws(&self.extended_attributes); - - first_pass - .create_basic_method( - &self - .arguments - .iter() - .map(|argument| argument.apply_typedefs(first_pass)) - .collect::>(), - self.name.as_ref(), - &self.return_type.apply_typedefs(first_pass), - self_name, - false, - throws, - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - - Ok(()) - } -} - -impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, - ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { - return Ok(()); - } - - let throws = util::throws(&self.extended_attributes); - - first_pass - .create_basic_method( - &self - .arguments - .iter() - .map(|argument| argument.apply_typedefs(first_pass)) - .collect::>(), - self.name.as_ref(), - &self.return_type.apply_typedefs(first_pass), - self_name, - true, - throws, - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - - Ok(()) - } -} - -impl<'a> WebidlParse<()> for webidl::ast::Enum { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - _: &FirstPassRecord<'_>, + _: &FirstPassRecord<'src>, (): (), ) -> Result<()> { + let variants = &self.values.body.list; program.imports.push(backend::ast::Import { module: None, version: None, js_namespace: None, kind: backend::ast::ImportKind::Enum(backend::ast::ImportEnum { vis: public(), - name: rust_ident(camel_case_ident(&self.name).as_str()), - variants: self - .variants + name: rust_ident(camel_case_ident(self.identifier.0).as_str()), + variants: variants .iter() - .map(|v| - if !v.is_empty() { - rust_ident(camel_case_ident(&v).as_str()) + .map(|v| { + if !v.0.is_empty() { + rust_ident(camel_case_ident(&v.0).as_str()) } else { rust_ident("None") } - ) + }) .collect(), - variant_values: self.variants.clone(), + variant_values: variants.iter().map(|v| v.0.to_string()).collect(), rust_attrs: vec![parse_quote!(#[derive(Copy, Clone, PartialEq, Debug)])], }), }); @@ -716,21 +696,24 @@ impl<'a> WebidlParse<()> for webidl::ast::Enum { } } -impl<'a> WebidlParse<&'a str> for webidl::ast::Const { +impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, + record: &FirstPassRecord<'src>, + self_name: &'src str, ) -> Result<()> { - let ty = webidl_const_ty_to_syn_ty(&self.type_.apply_typedefs(first_pass)); + let ty = match self.const_type.to_syn_type(record, TypePosition::Return) { + Some(s) => s, + None => return Ok(()), + }; program.consts.push(backend::ast::Const { vis: public(), - name: rust_ident(self.name.to_shouty_snake_case().as_str()), + name: rust_ident(self.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(&self.value), + value: webidl_const_v_to_backend_const_v(&self.const_value), }); Ok(()) diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 26c1e5fc..50c05901 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1,12 +1,16 @@ -use std::iter::{self, FromIterator}; +use std::iter::FromIterator; use backend; use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident}; use heck::{CamelCase, SnakeCase}; use proc_macro2::Ident; use syn; -use webidl; -use webidl::ast::ExtendedAttribute; +use weedle; +use weedle::attribute::{ExtendedAttributeList, ExtendedAttribute}; +use weedle::argument::{Argument, SingleArgument}; +use weedle::common::Identifier; +use weedle::types::*; +use weedle::literal::{ConstValue, FloatLit, IntegerLit}; use first_pass::FirstPassRecord; @@ -34,35 +38,392 @@ pub fn mdn_doc(class: &str, method: Option<&str>) -> String { format!("[Documentation]({})", link).into() } -/// For a webidl const type node, get the corresponding syn type node. -pub fn webidl_const_ty_to_syn_ty(ty: &webidl::ast::ConstType) -> syn::Type { - use webidl::ast::ConstType::*; +pub(crate) trait ToSynType<'src> { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option; +} - // similar to webidl_ty_to_syn_ty - match ty { - Boolean => ident_ty(raw_ident("bool")), - Byte => ident_ty(raw_ident("i8")), - Octet => ident_ty(raw_ident("u8")), - RestrictedDouble | UnrestrictedDouble => ident_ty(raw_ident("f64")), - RestrictedFloat | UnrestrictedFloat => ident_ty(raw_ident("f32")), - SignedLong => ident_ty(raw_ident("i32")), - SignedLongLong => ident_ty(raw_ident("i64")), - SignedShort => ident_ty(raw_ident("i16")), - UnsignedLong => ident_ty(raw_ident("u32")), - UnsignedLongLong => ident_ty(raw_ident("u64")), - UnsignedShort => ident_ty(raw_ident("u16")), - Identifier(ref id) => ident_ty(rust_ident(id)), +impl<'src, T: ToSynType<'src>> ToSynType<'src> for MayBeNull { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + let ty = self.type_.to_syn_type(record, pos)?; + if self.q_mark.is_some() { + Some(option_ty(ty)) + } else { + Some(ty) + } } } +impl<'src> ToSynType<'src> for ConstType<'src> { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + match self { + ConstType::Integer(l) => l.to_syn_type(record, pos), + ConstType::FloatingPoint(l) => l.to_syn_type(record, pos), + ConstType::Boolean(l) => l.to_syn_type(record, pos), + ConstType::Byte(l) => l.to_syn_type(record, pos), + ConstType::Octet(l) => l.to_syn_type(record, pos), + ConstType::Identifier(l) => l.to_syn_type(record, pos), + } + } +} + +impl<'src> ToSynType<'src> for IntegerType { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + match self { + IntegerType::LongLong(l) => l.to_syn_type(record, pos), + IntegerType::Long(l) => l.to_syn_type(record, pos), + IntegerType::Short(l) => l.to_syn_type(record, pos), + } + } +} + +impl<'src> ToSynType<'src> for ShortType { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + if self.unsigned.is_some() { + Some(ident_ty(raw_ident("u16"))) + } else { + Some(ident_ty(raw_ident("i16"))) + } + } +} + +impl<'src> ToSynType<'src> for LongType { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + if self.unsigned.is_some() { + Some(ident_ty(raw_ident("u32"))) + } else { + Some(ident_ty(raw_ident("i32"))) + } + } +} + +impl<'src> ToSynType<'src> for LongLongType { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + if self.unsigned.is_some() { + Some(ident_ty(raw_ident("u64"))) + } else { + Some(ident_ty(raw_ident("i64"))) + } + } +} + +impl<'src> ToSynType<'src> for FloatingPointType { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + match self { + FloatingPointType::Float(_) => Some(ident_ty(raw_ident("f32"))), + FloatingPointType::Double(_) => Some(ident_ty(raw_ident("f64"))), + } + } +} + +impl<'src> ToSynType<'src> for weedle::term::Boolean { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + Some(ident_ty(raw_ident("bool"))) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Byte { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + Some(ident_ty(raw_ident("i8"))) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Octet { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + Some(ident_ty(raw_ident("u8"))) + } +} + +impl<'src> ToSynType<'src> for weedle::common::Identifier<'src> { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + if let Some(other) = record.typedefs.get(&self.0) { + return other.to_syn_type(record, pos) + } + // A reference to a type by name becomes the same thing in the + // bindings. + let ty = ident_ty(rust_ident(camel_case_ident(self.0).as_str())); + Some(if record.interfaces.contains_key(self.0) { + if pos == TypePosition::Argument { + shared_ref(ty) + } else { + ty + } + } else if record.dictionaries.contains(self.0) { + ty + } else if record.enums.contains(self.0) { + ty + } else { + warn!("unrecognized type {}", self.0); + ty + }) + } +} + +impl<'src> ToSynType<'src> for weedle::term::DOMString { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + // strings -> `&str` for arguments and `String` for return + Some(match pos { + TypePosition::Argument => shared_ref(ident_ty(raw_ident("str"))), + TypePosition::Return => ident_ty(raw_ident("String")), + }) + } +} + +impl<'src> ToSynType<'src> for weedle::term::ByteString { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + // ByteString maps to String in JS - + // https://developer.mozilla.org/en-US/docs/Web/API/ByteString + weedle::term::DOMString.to_syn_type(record, pos) + } +} + +impl<'src> ToSynType<'src> for weedle::term::USVString { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + // USVString maps to String in JS - + // https://developer.mozilla.org/en-US/docs/Web/API/USVString + weedle::term::DOMString.to_syn_type(record, pos) + } +} + +// Array type is borrowed for arguments (`&[T]`) and owned for return value (`Vec`). +fn array(base_ty: &str, pos: TypePosition) -> syn::Type { + match pos { + TypePosition::Argument => { + shared_ref(slice_ty(ident_ty(raw_ident(base_ty)))) + } + TypePosition::Return => { + vec_ty(ident_ty(raw_ident(base_ty))) + } + } +} + +impl<'src> ToSynType<'src> for weedle::term::Float32Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("f32", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Float64Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("f64", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Int8Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("i8", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Int16Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("i16", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Int32Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("i32", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Uint8Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("u8", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Uint8ClampedArray { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("u8", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Uint16Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("u16", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Uint32Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("u32", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::ArrayBuffer { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + let path = vec![rust_ident("js_sys"), rust_ident("ArrayBuffer")]; + Some(leading_colon_path_ty(path)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Object { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + let path = vec![rust_ident("js_sys"), rust_ident("Object")]; + Some(leading_colon_path_ty(path)) + } +} + +impl<'src> ToSynType<'src> for weedle::types::Type<'src> { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + use weedle::types::NonAnyType::*; + let single = match self { + Type::Single(s) => s, + Type::Union(_) => return None, + }; + + let ty = match single { + // `any` becomes `::wasm_bindgen::JsValue`. + SingleType::Any(_) => { + let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; + return Some(leading_colon_path_ty(path)) + } + SingleType::NonAny(other) => other, + }; + + match ty { + Boolean(s) => s.to_syn_type(record, pos), + Octet(s) => s.to_syn_type(record, pos), + Byte(s) => s.to_syn_type(record, pos), + Identifier(s) => s.to_syn_type(record, pos), + Integer(s) => s.to_syn_type(record, pos), + FloatingPoint(s) => s.to_syn_type(record, pos), + + Float32Array(s) => s.to_syn_type(record, pos), + Float64Array(s) => s.to_syn_type(record, pos), + Int8Array(s) => s.to_syn_type(record, pos), + Int16Array(s) => s.to_syn_type(record, pos), + Int32Array(s) => s.to_syn_type(record, pos), + Uint8Array(s) => s.to_syn_type(record, pos), + Uint8ClampedArray(s) => s.to_syn_type(record, pos), + Uint16Array(s) => s.to_syn_type(record, pos), + Uint32Array(s) => s.to_syn_type(record, pos), + + DOMString(s) => s.to_syn_type(record, pos), + ByteString(s) => s.to_syn_type(record, pos), + USVString(s) => s.to_syn_type(record, pos), + ArrayBuffer(b) => b.to_syn_type(record, pos), + Object(o) => o.to_syn_type(record, pos), + + // Support for these types is not yet implemented, so skip + // generating any bindings for this function. + | DataView(_) + | Error(_) + | FrozenArrayType(_) + | Promise(_) + | RecordType(..) + | Sequence(_) + | Symbol(_) => { + None + } + } + } +} + + /// Map a webidl const value to the correct wasm-bindgen const value -pub fn webidl_const_v_to_backend_const_v(v: &webidl::ast::ConstValue) -> backend::ast::ConstValue { +pub fn webidl_const_v_to_backend_const_v(v: &ConstValue) -> backend::ast::ConstValue { + use std::f64::{NEG_INFINITY, INFINITY, NAN}; + use backend::ast; + match *v { - webidl::ast::ConstValue::BooleanLiteral(b) => backend::ast::ConstValue::BooleanLiteral(b), - webidl::ast::ConstValue::FloatLiteral(f) => backend::ast::ConstValue::FloatLiteral(f), - webidl::ast::ConstValue::SignedIntegerLiteral(i) => backend::ast::ConstValue::SignedIntegerLiteral(i), - webidl::ast::ConstValue::UnsignedIntegerLiteral(u) => backend::ast::ConstValue::UnsignedIntegerLiteral(u), - webidl::ast::ConstValue::Null => backend::ast::ConstValue::Null, + ConstValue::Boolean(b) => ast::ConstValue::BooleanLiteral(b.0), + ConstValue::Float(FloatLit::NegInfinity(_)) => { + ast::ConstValue::FloatLiteral(NEG_INFINITY) + } + ConstValue::Float(FloatLit::Infinity(_)) => { + ast::ConstValue::FloatLiteral(INFINITY) + } + ConstValue::Float(FloatLit::NaN(_)) => { + ast::ConstValue::FloatLiteral(NAN) + } + ConstValue::Float(FloatLit::Value(s)) => { + ast::ConstValue::FloatLiteral(s.0.parse().unwrap()) + } + ConstValue::Integer(lit) => { + let mklit = |orig_text: &str, base: u32, offset: usize| { + let (negative, text) = if orig_text.starts_with("-") { + (true, &orig_text[1..]) + } else { + (false, orig_text) + }; + if text == "0" { + return ast::ConstValue::SignedIntegerLiteral(0) + } + let text = &text[offset..]; + let n = u64::from_str_radix(text, base) + .unwrap_or_else(|_| panic!("literal too big: {}", orig_text)); + if negative { + let n = if n > (i64::min_value() as u64).wrapping_neg() { + panic!("literal too big: {}", orig_text) + } else { + n.wrapping_neg() as i64 + }; + ast::ConstValue::SignedIntegerLiteral(n) + } else { + ast::ConstValue::UnsignedIntegerLiteral(n) + } + }; + match lit { + IntegerLit::Hex(h) => mklit(h.0, 16, 2), // leading 0x + IntegerLit::Oct(h) => mklit(h.0, 8, 1), // leading 0 + IntegerLit::Dec(h) => mklit(h.0, 10, 0), + } + } + ConstValue::Null(_) => ast::ConstValue::Null, } } @@ -135,6 +496,22 @@ fn vec_ty(t: syn::Type) -> syn::Type { ty.into() } +/// From `T` create `Option` +fn option_ty(t: syn::Type) -> syn::Type { + let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + colon2_token: None, + lt_token: Default::default(), + args: FromIterator::from_iter(vec![syn::GenericArgument::Type(t)]), + gt_token: Default::default(), + }); + + let ident = raw_ident("Option"); + let seg = syn::PathSegment { ident, arguments }; + let path: syn::Path = seg.into(); + let ty = syn::TypePath { qself: None, path }; + ty.into() +} + /// Possible positions for a type in a function signature. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum TypePosition { @@ -142,350 +519,280 @@ pub enum TypePosition { Return, } -fn type_kind_to_const_type(type_kind: &webidl::ast::TypeKind) -> webidl::ast::ConstType { - match type_kind { - webidl::ast::TypeKind::Boolean => webidl::ast::ConstType::Boolean, - webidl::ast::TypeKind::Byte => webidl::ast::ConstType::Byte, - webidl::ast::TypeKind::Identifier(identifier) => webidl::ast::ConstType::Identifier(identifier.clone()), - webidl::ast::TypeKind::Octet => webidl::ast::ConstType::Octet, - webidl::ast::TypeKind::RestrictedDouble => webidl::ast::ConstType::RestrictedDouble, - webidl::ast::TypeKind::RestrictedFloat => webidl::ast::ConstType::RestrictedFloat, - webidl::ast::TypeKind::SignedLong => webidl::ast::ConstType::SignedLong, - webidl::ast::TypeKind::SignedLongLong => webidl::ast::ConstType::SignedLongLong, - webidl::ast::TypeKind::SignedShort => webidl::ast::ConstType::SignedShort, - webidl::ast::TypeKind::UnrestrictedDouble => webidl::ast::ConstType::UnrestrictedDouble, - webidl::ast::TypeKind::UnrestrictedFloat => webidl::ast::ConstType::UnrestrictedFloat, - webidl::ast::TypeKind::UnsignedLong => webidl::ast::ConstType::UnsignedLong, - webidl::ast::TypeKind::UnsignedLongLong => webidl::ast::ConstType::UnsignedLongLong, - webidl::ast::TypeKind::UnsignedShort => webidl::ast::ConstType::UnsignedShort, - _ => panic!("can not convert TypeKind to ConstType: {:#?}", type_kind), - } -} - -/// Implemented on an AST type node to apply typedefs. -pub(crate) trait ApplyTypedefs { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self; -} - -impl ApplyTypedefs for webidl::ast::Type { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self { - webidl::ast::Type { - extended_attributes: self.extended_attributes.clone(), - kind: self.kind.apply_typedefs(record), - nullable: self.nullable, - } - } -} - -impl ApplyTypedefs for webidl::ast::ReturnType { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self { - match self { - webidl::ast::ReturnType::NonVoid(ty) => - webidl::ast::ReturnType::NonVoid(Box::new(ty.apply_typedefs(record))), - _ => self.clone(), - } - } -} - -impl ApplyTypedefs for webidl::ast::StringType { - fn apply_typedefs<'a>(&self, _: &FirstPassRecord<'a>) -> Self { - *self - } -} - -impl ApplyTypedefs for webidl::ast::ConstType { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self { - match self { - webidl::ast::ConstType::Identifier(identifier) => - record - .typedefs - .get(identifier) - .map(|ty| type_kind_to_const_type(&ty.kind)) - .unwrap_or(self.clone()), - _ => self.clone(), - } - } -} - -impl ApplyTypedefs for webidl::ast::TypeKind { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self { - match self { - webidl::ast::TypeKind::FrozenArray(ty) => - webidl::ast::TypeKind::FrozenArray(Box::new(ty.apply_typedefs(record))), - webidl::ast::TypeKind::Identifier(identifier) => record - .typedefs - .get(identifier) - .map(|ty| ty.kind.clone()) - .unwrap_or(self.clone()), - webidl::ast::TypeKind::Promise(ty) => - webidl::ast::TypeKind::Promise(ty.apply_typedefs(record)), - webidl::ast::TypeKind::Record(string_type, ty) => webidl::ast::TypeKind::Record( - string_type.apply_typedefs(record), - Box::new(ty.apply_typedefs(record)), - ), - webidl::ast::TypeKind::Union(types) => webidl::ast::TypeKind::Union( - types - .iter() - .map(|ty| Box::new(ty.apply_typedefs(record))) - .collect(), - ), - _ => self.clone(), - } - } -} - -impl ApplyTypedefs for webidl::ast::Argument { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self { - webidl::ast::Argument { - extended_attributes: self.extended_attributes.clone(), - default: self.default.clone(), - name: self.name.clone(), - optional: self.optional, - type_: Box::new(self.type_.apply_typedefs(record)), - variadic: self.variadic, - } - } -} - /// Implemented on an AST type node to generate a snake case name. trait TypeToString { - fn type_to_string(&self) -> String; + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String); } -impl TypeToString for webidl::ast::Type { - fn type_to_string(&self) -> String { - if self.nullable { - "opt_".to_owned() + &self.kind.type_to_string() - } else { - self.kind.type_to_string() +impl TypeToString for MayBeNull { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + if self.q_mark.is_some() { + dst.push_str("opt_"); } + self.type_.type_to_string(record, dst); } } -impl TypeToString for webidl::ast::ReturnType { - fn type_to_string(&self) -> String { +impl<'src> TypeToString for weedle::types::ReturnType<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { match self { - webidl::ast::ReturnType::NonVoid(ty) => (*ty).type_to_string(), - webidl::ast::ReturnType::Void => "void".to_owned(), + weedle::types::ReturnType::Type(ty) => (*ty).type_to_string(record, dst), + weedle::types::ReturnType::Void(_) => dst.push_str("void"), } } } -impl TypeToString for webidl::ast::StringType { - fn type_to_string(&self) -> String { +impl TypeToString for weedle::types::StringType { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { match self { - webidl::ast::StringType::ByteString => "byte_str".to_owned(), - webidl::ast::StringType::DOMString => "dom_str".to_owned(), - webidl::ast::StringType::USVString => "usv_str".to_owned(), + weedle::types::StringType::Byte(_) => dst.push_str("byte_str"), + weedle::types::StringType::DOM(_) => dst.push_str("dom_str"), + weedle::types::StringType::USV(_) => dst.push_str("usv_str"), } } } -impl TypeToString for webidl::ast::TypeKind { - fn type_to_string(&self) -> String { +impl TypeToString for weedle::term::Byte { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("i8"); + } +} + +impl TypeToString for weedle::term::Octet { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("u8"); + } +} + +impl TypeToString for weedle::term::Boolean { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("bool"); + } +} + +impl TypeToString for weedle::term::USVString { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("usv_str"); + } +} + +impl TypeToString for weedle::term::ByteString { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("byte_str"); + } +} + +impl TypeToString for weedle::term::DOMString { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("dom_str"); + } +} + +impl TypeToString for weedle::term::Float32Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("f32_array"); + } +} + +impl TypeToString for weedle::term::Float64Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("f64_array"); + } +} + +impl TypeToString for weedle::term::Int8Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("i8_array"); + } +} + +impl TypeToString for weedle::term::Int16Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("i16_array"); + } +} + +impl TypeToString for weedle::term::Int32Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("i32_array"); + } +} + +impl TypeToString for weedle::term::Uint8Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("u8_array"); + } +} + +impl TypeToString for weedle::term::Uint8ClampedArray { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("u8_clamped_array"); + } +} + +impl TypeToString for weedle::term::Uint16Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("u16_array"); + } +} + +impl TypeToString for weedle::term::Uint32Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("u32_array"); + } +} + +impl<'src> TypeToString for weedle::common::Identifier<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + match record.typedefs.get(self.0) { + Some(other) => other.type_to_string(record, dst), + None => dst.push_str(&self.0.to_snake_case()), + } + } +} + +impl TypeToString for IntegerType { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { match self { - webidl::ast::TypeKind::Any => "any".to_owned(), - webidl::ast::TypeKind::ArrayBuffer => "array_buffer".to_owned(), - webidl::ast::TypeKind::Boolean => "bool".to_owned(), - webidl::ast::TypeKind::Byte => "i8".to_owned(), - webidl::ast::TypeKind::ByteString => "byte_str".to_owned(), - webidl::ast::TypeKind::DOMString => "dom_str".to_owned(), - webidl::ast::TypeKind::DataView => "data_view".to_owned(), - webidl::ast::TypeKind::Error => "error".to_owned(), - webidl::ast::TypeKind::Float32Array => "f32_array".to_owned(), - webidl::ast::TypeKind::Float64Array => "f64_array".to_owned(), - webidl::ast::TypeKind::FrozenArray(ty) => "frozen_array_of_".to_owned() + &ty.type_to_string(), - webidl::ast::TypeKind::Identifier(identifier) => identifier.to_snake_case(), - webidl::ast::TypeKind::Int16Array => "i16_array".to_owned(), - webidl::ast::TypeKind::Int32Array => "i32_array".to_owned(), - webidl::ast::TypeKind::Int8Array => "i8_array".to_owned(), - webidl::ast::TypeKind::Octet => "u8".to_owned(), - webidl::ast::TypeKind::Object => "object".to_owned(), - webidl::ast::TypeKind::Promise(ty) => "promise_of_".to_owned() + &(*ty).type_to_string(), - webidl::ast::TypeKind::Record(string_type, ty) => format!( - "record_from_{}_to_{}", - string_type.type_to_string(), - (*ty).type_to_string() - ), - webidl::ast::TypeKind::RestrictedDouble => "restricted_f64".to_owned(), - webidl::ast::TypeKind::RestrictedFloat => "restricted_f32".to_owned(), - webidl::ast::TypeKind::Sequence(ty) => "sequence_of_".to_owned() + &ty.type_to_string(), - webidl::ast::TypeKind::SignedLong => "i32".to_owned(), - webidl::ast::TypeKind::SignedLongLong => "i64".to_owned(), - webidl::ast::TypeKind::SignedShort => "i16".to_owned(), - webidl::ast::TypeKind::Symbol => "symbol".to_owned(), - webidl::ast::TypeKind::USVString => "usv_str".to_owned(), - webidl::ast::TypeKind::Uint16Array => "u16_array".to_owned(), - webidl::ast::TypeKind::Uint32Array => "u32_array".to_owned(), - webidl::ast::TypeKind::Uint8Array => "u8_array".to_owned(), - webidl::ast::TypeKind::Uint8ClampedArray => "u8_clamped_array".to_owned(), - webidl::ast::TypeKind::Union(types) => "union_of_".to_owned() + &types - .iter() - .map(|ty| (*ty).type_to_string()) - .collect::>() - .join("_and_"), - webidl::ast::TypeKind::UnrestrictedDouble => "unrestricted_f64".to_owned(), - webidl::ast::TypeKind::UnrestrictedFloat => "unrestricted_f32".to_owned(), - webidl::ast::TypeKind::UnsignedLong => "u32".to_owned(), - webidl::ast::TypeKind::UnsignedLongLong => "u64".to_owned(), - webidl::ast::TypeKind::UnsignedShort => "u16".to_owned(), + IntegerType::LongLong(l) if l.unsigned.is_some() => dst.push_str("u64"), + IntegerType::LongLong(_) => dst.push_str("i64"), + IntegerType::Long(l) if l.unsigned.is_some() => dst.push_str("u32"), + IntegerType::Long(_) => dst.push_str("i32"), + IntegerType::Short(l) if l.unsigned.is_some() => dst.push_str("u16"), + IntegerType::Short(_) => dst.push_str("i16"), } } } -impl<'a> FirstPassRecord<'a> { - /// Use information from the first pass to work out the correct Rust type to use for - /// a given WebIDL type. - pub fn webidl_ty_to_syn_ty( - &self, - ty: &webidl::ast::Type, - pos: TypePosition, - ) -> Option { - // Array type is borrowed for arguments (`&[T]`) and owned for return value (`Vec`). - let array = |base_ty: &str| { - match pos { - TypePosition::Argument => { - shared_ref(slice_ty(ident_ty(raw_ident(base_ty)))) - } - TypePosition::Return => { - vec_ty(ident_ty(raw_ident(base_ty))) - } - } +impl TypeToString for FloatingPointType { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + match self { + FloatingPointType::Float(_) => dst.push_str("f32"), + FloatingPointType::Double(_) => dst.push_str("f64"), + } + } +} + +impl TypeToString for weedle::term::ArrayBuffer { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("array_buffer"); + } +} + +impl TypeToString for weedle::term::Symbol { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("symbol"); + } +} + +impl TypeToString for weedle::term::Object { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("object"); + } +} + +impl TypeToString for weedle::term::DataView { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("data_view"); + } +} + +impl TypeToString for weedle::term::Error { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("error"); + } +} + +impl<'src> TypeToString for weedle::types::SequenceType<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + dst.push_str("seq_"); + self.generics.body.type_to_string(record, dst); + } +} + +impl<'src> TypeToString for weedle::types::PromiseType<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + dst.push_str("promise_"); + self.generics.body.type_to_string(record, dst); + } +} + +impl<'src> TypeToString for weedle::types::FrozenArrayType<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + dst.push_str("frozen_array_"); + self.generics.body.type_to_string(record, dst); + } +} + +impl<'src> TypeToString for weedle::types::RecordType<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + dst.push_str("record_from_"); + self.generics.body.0.type_to_string(record, dst); + dst.push_str("_to_"); + self.generics.body.2.type_to_string(record, dst); + } +} + +impl<'a> TypeToString for weedle::types::Type<'a> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + use weedle::types::NonAnyType::*; + + let single = match self { + Type::Single(s) => s, + Type::Union(_) => panic!("unions not supported"), }; - let base_ty = match ty.kind { - // `any` becomes `::wasm_bindgen::JsValue`. - webidl::ast::TypeKind::Any => { - leading_colon_path_ty(vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]) - } - - // A reference to a type by name becomes the same thing in the - // bindings. - webidl::ast::TypeKind::Identifier(ref id) => { - let ty = ident_ty(rust_ident(camel_case_ident(&id).as_str())); - if self.interfaces.contains_key(id) { - if pos == TypePosition::Argument { - shared_ref(ty) - } else { - ty - } - } else if self.dictionaries.contains(id) { - ty - } else if self.enums.contains(id) { - ty - } else { - warn!("unrecognized type {}", id); - ty - } - } - - // Scalars. - webidl::ast::TypeKind::Boolean => ident_ty(raw_ident("bool")), - webidl::ast::TypeKind::Byte => ident_ty(raw_ident("i8")), - webidl::ast::TypeKind::Octet => ident_ty(raw_ident("u8")), - webidl::ast::TypeKind::RestrictedDouble | webidl::ast::TypeKind::UnrestrictedDouble => { - ident_ty(raw_ident("f64")) - } - webidl::ast::TypeKind::RestrictedFloat | webidl::ast::TypeKind::UnrestrictedFloat => { - ident_ty(raw_ident("f32")) - } - webidl::ast::TypeKind::SignedLong => ident_ty(raw_ident("i32")), - webidl::ast::TypeKind::SignedLongLong => ident_ty(raw_ident("i64")), - webidl::ast::TypeKind::SignedShort => ident_ty(raw_ident("i16")), - webidl::ast::TypeKind::UnsignedLong => ident_ty(raw_ident("u32")), - webidl::ast::TypeKind::UnsignedLongLong => ident_ty(raw_ident("u64")), - webidl::ast::TypeKind::UnsignedShort => ident_ty(raw_ident("u16")), - - webidl::ast::TypeKind::Float32Array => array("f32"), - webidl::ast::TypeKind::Float64Array => array("f64"), - webidl::ast::TypeKind::Int8Array => array("i8"), - webidl::ast::TypeKind::Int16Array => array("i16"), - webidl::ast::TypeKind::Int32Array => array("i32"), - webidl::ast::TypeKind::Uint8Array => array("u8"), - webidl::ast::TypeKind::Uint8ClampedArray => array("u8"), - webidl::ast::TypeKind::Uint16Array => array("u16"), - webidl::ast::TypeKind::Uint32Array => array("u32"), - - // strings -> `&str` for arguments and `String` for return - // - // Note that DOMString mostly makes sense here, ByteString maps to - // String in JS [1], along with USVString - // - // [1]: https://developer.mozilla.org/en-US/docs/Web/API/ByteString - // [2]: https://developer.mozilla.org/en-US/docs/Web/API/USVString - webidl::ast::TypeKind::DOMString - | webidl::ast::TypeKind::ByteString - | webidl::ast::TypeKind::USVString => { - match pos { - TypePosition::Argument => shared_ref(ident_ty(raw_ident("str"))), - TypePosition::Return => ident_ty(raw_ident("String")), - } - } - - // This seems like a "naively correct" mapping, but the online docs - // are a bit scary in this regard... - // - // https://heycam.github.io/webidl/#es-buffer-source-types - webidl::ast::TypeKind::ArrayBuffer => { - leading_colon_path_ty(vec![rust_ident("js_sys"), rust_ident("ArrayBuffer")]) - } - - // The WebIDL `object` maps to the ECMAScript `Object` - // - // https://heycam.github.io/webidl/#es-object - webidl::ast::TypeKind::Object => { - leading_colon_path_ty(vec![rust_ident("js_sys"), rust_ident("Object")]) - } - - // Support for these types is not yet implemented, so skip - // generating any bindings for this function. - | webidl::ast::TypeKind::DataView - | webidl::ast::TypeKind::Error - | webidl::ast::TypeKind::FrozenArray(_) - | webidl::ast::TypeKind::Promise(_) - | webidl::ast::TypeKind::Record(..) - | webidl::ast::TypeKind::Sequence(_) - | webidl::ast::TypeKind::Symbol - | webidl::ast::TypeKind::Union(_) => { - return None; - } + let ty = match single { + SingleType::Any(_) => return dst.push_str("any"), + SingleType::NonAny(other) => other, }; - // Map nullable to an option. - if ty.nullable { - let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { - colon2_token: None, - lt_token: Default::default(), - args: FromIterator::from_iter(vec![ - syn::GenericArgument::Type(base_ty), - ]), - gt_token: Default::default(), - }); + match ty { + Boolean(s) => s.type_to_string(record, dst), + Octet(s) => s.type_to_string(record, dst), + Byte(s) => s.type_to_string(record, dst), + Identifier(s) => s.type_to_string(record, dst), + Integer(s) => s.type_to_string(record, dst), + FloatingPoint(s) => s.type_to_string(record, dst), - let ident = raw_ident("Option"); - let seg = syn::PathSegment { ident, arguments }; - let path: syn::Path = seg.into(); - let ty = syn::TypePath { qself: None, path }; - Some(ty.into()) - } else { - Some(base_ty) + Float32Array(s) => s.type_to_string(record, dst), + Float64Array(s) => s.type_to_string(record, dst), + Int8Array(s) => s.type_to_string(record, dst), + Int16Array(s) => s.type_to_string(record, dst), + Int32Array(s) => s.type_to_string(record, dst), + Uint8Array(s) => s.type_to_string(record, dst), + Uint8ClampedArray(s) => s.type_to_string(record, dst), + Uint16Array(s) => s.type_to_string(record, dst), + Uint32Array(s) => s.type_to_string(record, dst), + + DOMString(s) => s.type_to_string(record, dst), + ByteString(s) => s.type_to_string(record, dst), + USVString(s) => s.type_to_string(record, dst), + ArrayBuffer(s) => s.type_to_string(record, dst), + + DataView(s) => s.type_to_string(record, dst), + Error(s) => s.type_to_string(record, dst), + FrozenArrayType(s) => s.type_to_string(record, dst), + Object(s) => s.type_to_string(record, dst), + Promise(s) => s.type_to_string(record, dst), + RecordType(s) => s.type_to_string(record, dst), + Sequence(s) => s.type_to_string(record, dst), + Symbol(s) => s.type_to_string(record, dst), } } +} +impl<'src> FirstPassRecord<'src> { /// Use the first pass to convert webidl function arguments to rust arguments. /// /// `kind` is whether the function is a method, in which case we would need a `self` /// parameter. - fn webidl_arguments_to_syn_arg_captured<'b, I>( + fn webidl_arguments_to_syn_arg_captured( &self, - arguments: I, + arguments: &[Argument], kind: &backend::ast::ImportFunctionKind, ) -> Option> - where - I: Iterator, { - let estimate = arguments.size_hint(); - let len = estimate.1.unwrap_or(estimate.0); let mut res = if let backend::ast::ImportFunctionKind::Method { ty, kind: @@ -495,25 +802,27 @@ impl<'a> FirstPassRecord<'a> { .. } = kind { - let mut res = Vec::with_capacity(len + 1); + let mut res = Vec::with_capacity(arguments.len() + 1); res.push(simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone()))); res } else { - Vec::with_capacity(len) + Vec::with_capacity(arguments.len()) }; - for (name, ty, variadic) in arguments { - if variadic { - warn!("Variadic arguments are not supported yet",); - return None; - } - - match self.webidl_ty_to_syn_ty(ty, TypePosition::Argument) { + for argument in arguments { + let argument = match argument { + Argument::Single(arg) => arg, + Argument::Variadic(_) => return None, + }; + match argument.type_.type_.to_syn_type(self, TypePosition::Argument) { None => { - warn!("Argument's type is not yet supported: {:?}", ty); + warn!("Argument's type is not yet supported: {:?}", argument); return None; } - Some(ty) => res.push(simple_fn_arg(rust_ident(&name.to_snake_case()), ty)), + Some(ty) => { + let name = argument.identifier.0.to_snake_case(); + res.push(simple_fn_arg(rust_ident(&name), ty)) + } } } @@ -521,39 +830,38 @@ impl<'a> FirstPassRecord<'a> { } /// Create a wasm-bindgen function, if possible. - pub fn create_function<'b, I>( + pub fn create_function( &self, name: &str, overloaded: bool, same_argument_names: bool, - arguments: I, + arguments: &[Argument], mut ret: Option, kind: backend::ast::ImportFunctionKind, structural: bool, catch: bool, doc_comment: Option, ) -> Option - where - I: Iterator, { - let arguments: Vec<_> = arguments.collect(); + let ast_arguments = self.webidl_arguments_to_syn_arg_captured(arguments, &kind)?; + let rust_name = rust_ident( &if overloaded && !arguments.is_empty() { - let argument_type_names = arguments - .iter() - .map(|&(name, ty, variadic)| { - if same_argument_names { - if variadic { - "variadic_".to_owned() + &ty.type_to_string() - } else { - ty.type_to_string() - } - } else { - name.to_snake_case() - } - }) - .collect::>() - .join("_and_"); + let mut argument_type_names = String::new(); + for arg in arguments { + let arg = match arg { + Argument::Single(single) => single, + Argument::Variadic(_) => return None, + }; + if argument_type_names.len() > 0 { + argument_type_names.push_str("_and_"); + } + if same_argument_names { + arg.type_.type_.type_to_string(self, &mut argument_type_names); + } else { + argument_type_names.push_str(&arg.identifier.0.to_snake_case()); + } + } if name == "new" { "with_".to_owned() + &argument_type_names } else { @@ -565,8 +873,6 @@ impl<'a> FirstPassRecord<'a> { ); let name = name.to_string(); - let arguments = self.webidl_arguments_to_syn_arg_captured(arguments.into_iter(), &kind)?; - let js_ret = ret.clone(); if catch { @@ -585,7 +891,7 @@ impl<'a> FirstPassRecord<'a> { Some(backend::ast::ImportFunction { function: backend::ast::Function { name, - arguments, + arguments: ast_arguments, ret, rust_attrs: vec![], rust_vis: public(), @@ -603,16 +909,16 @@ impl<'a> FirstPassRecord<'a> { /// Create a wasm-bindgen method, if possible. pub fn create_basic_method( &self, - arguments: &[webidl::ast::Argument], - name: Option<&String>, - return_type: &webidl::ast::ReturnType, + arguments: &[weedle::argument::Argument], + name: Option<&str>, + return_type: &weedle::types::ReturnType, self_name: &str, is_static: bool, catch: bool, ) -> Option { let (overloaded, same_argument_names) = self.get_operation_overloading( arguments, - ::first_pass::OperationId::Operation(name.cloned()), + ::first_pass::OperationId::Operation(name), self_name, ); @@ -634,9 +940,9 @@ impl<'a> FirstPassRecord<'a> { }; let ret = match return_type { - webidl::ast::ReturnType::Void => None, - webidl::ast::ReturnType::NonVoid(ty) => { - match self.webidl_ty_to_syn_ty(ty, TypePosition::Return) { + weedle::types::ReturnType::Void(_) => None, + weedle::types::ReturnType::Type(ty) => { + match ty.to_syn_type(self, TypePosition::Return) { None => { warn!("Operation's return type is not yet supported: {:?}", ty); return None; @@ -651,9 +957,7 @@ impl<'a> FirstPassRecord<'a> { &name, overloaded, same_argument_names, - arguments - .iter() - .map(|arg| (&*arg.name, &*arg.type_, arg.variadic)), + arguments, ret, kind, self @@ -670,47 +974,45 @@ impl<'a> FirstPassRecord<'a> { /// whether there overloads with same argument names for given argument types pub fn get_operation_overloading( &self, - arguments: &[webidl::ast::Argument], + arguments: &[weedle::argument::Argument], id: ::first_pass::OperationId, self_name: &str, ) -> (bool, bool) { - self - .interfaces - .get(self_name) - .map(|interface_data| { - interface_data - .operations - .get(&id) - .map(|operation_data| - ( - operation_data.overloaded, - *operation_data - .argument_names_same - .get( - &arguments - .iter() - .map(|argument| argument.name.clone()) - .collect::>() - ) - .unwrap_or(&false) - ) - ) - .unwrap_or((false, false)) - }) - .unwrap_or((false, false)) + let data = match self.interfaces.get(self_name) { + Some(data) => data, + None => return (false, false), + }; + let data = match data.operations.get(&id) { + Some(data) => data, + None => return (false, false), + }; + let mut names = Vec::with_capacity(arguments.len()); + for arg in arguments { + match arg { + Argument::Single(arg) => names.push(arg.identifier.0), + Argument::Variadic(_) => return (false, false), + } + } + ( + data.overloaded, + *data + .argument_names_same + .get(&names) + .unwrap_or(&false) + ) } /// Create a wasm-bindgen getter method, if possible. pub fn create_getter( &self, name: &str, - ty: &webidl::ast::Type, + ty: &weedle::types::Type, self_name: &str, is_static: bool, is_structural: bool, catch: bool, ) -> Option { - let ret = match self.webidl_ty_to_syn_ty(ty, TypePosition::Return) { + let ret = match ty.to_syn_type(self, TypePosition::Return) { None => { warn!("Attribute's type does not yet support reading: {:?}", ty); return None; @@ -728,14 +1030,14 @@ impl<'a> FirstPassRecord<'a> { }; let doc_comment = Some(format!("The `{}` getter\n\n{}", name, mdn_doc(self_name, Some(name)))); - self.create_function(name, false, false, iter::empty(), ret, kind, is_structural, catch, doc_comment) + self.create_function(name, false, false, &[], ret, kind, is_structural, catch, doc_comment) } /// Create a wasm-bindgen setter method, if possible. pub fn create_setter( &self, name: &str, - ty: &webidl::ast::Type, + ty: weedle::types::Type, self_name: &str, is_static: bool, is_structural: bool, @@ -755,7 +1057,16 @@ impl<'a> FirstPassRecord<'a> { &format!("set_{}", name), false, false, - iter::once((name, ty, false)), + &[Argument::Single(SingleArgument { + attributes: None, + optional: None, + type_: AttributedType { + attributes: None, + type_: ty, + }, + identifier: Identifier(name), + default: None, + })], None, kind, is_structural, @@ -766,41 +1077,35 @@ impl<'a> FirstPassRecord<'a> { } /// Search for an attribute by name in some webidl object's attributes. -fn has_named_attribute(ext_attrs: &[Box], attribute: &str) -> bool { - ext_attrs.iter().any(|attr| match &**attr { - ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => { - name == attribute - } +fn has_named_attribute(list: &Option, attribute: &str) -> bool { + let list = match list { + Some(list) => list, + None => return false, + }; + list.body.list.iter().any(|attr| match attr { + ExtendedAttribute::NoArgs(name) => (name.0).0 == attribute, _ => false, }) } /// ChromeOnly is for things that are only exposed to privileged code in Firefox. -pub fn is_chrome_only(ext_attrs: &[Box]) -> bool { +pub fn is_chrome_only(ext_attrs: &Option) -> bool { has_named_attribute(ext_attrs, "ChromeOnly") } /// Whether a webidl object is marked as a no interface object. -pub fn is_no_interface_object(ext_attrs: &[Box]) -> bool { +pub fn is_no_interface_object(ext_attrs: &Option) -> bool { has_named_attribute(ext_attrs, "NoInterfaceObject") } /// Whether a webidl object is marked as structural. -pub fn is_structural(attrs: &[Box]) -> bool { - attrs.iter().any(|attr| match &**attr { - ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => { - name == "Unforgeable" - } - _ => false, - }) +pub fn is_structural(attrs: &Option) -> bool { + has_named_attribute(attrs, "Unforgeable") } /// Whether a webidl object is marked as throwing. -pub fn throws(attrs: &[Box]) -> bool { - attrs.iter().any(|attr| match &**attr { - ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => name == "Throws", - _ => false, - }) +pub fn throws(attrs: &Option) -> bool { + has_named_attribute(attrs, "Throws") } /// Create a syn `pub` token