From 5b952f20819a3be5025f207a64b9e92c42140827 Mon Sep 17 00:00:00 2001 From: "R. Andrew Ohana" Date: Tue, 10 Jul 2018 22:59:59 -0700 Subject: [PATCH 1/3] webidl: add support for partial interfaces and mixins This is a major change to how webidl is processed. This adds a two phase process, where the first phase records the names of various types and indexes the mixins (and might do more in the future). The actual program building happens in the second phase. As part of this, this also makes it so that interface objects are passed by reference, rather than by value. The spec isn't exactly clear on this, but Mozilla's C++ reflection suggestions seem to indicate that they should be passed by reference (see https://developer.mozilla.org/en-US/docs/Mozilla/WebIDL_bindings). --- crates/webidl/src/lib.rs | 432 +++++++++++++++------- crates/webidl/src/util.rs | 587 ++++++++++++++++-------------- crates/webidl/tests/all/simple.rs | 137 ++++++- 3 files changed, 744 insertions(+), 412 deletions(-) diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index e54e08bb9..511239aa4 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -24,6 +24,7 @@ use std::collections::BTreeSet; use std::fs; use std::io::{self, Read}; use std::iter::FromIterator; +use std::mem; use std::path::Path; use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports}; @@ -32,10 +33,7 @@ use failure::ResultExt; use heck::CamelCase; use quote::ToTokens; -use util::{ - create_basic_method, create_function, create_getter, create_setter, webidl_ty_to_syn_ty, - TypePosition, -}; +use util::{public, FirstPass, TypePosition}; /// Either `Ok(t)` or `Err(failure::Error)`. pub type Result = ::std::result::Result; @@ -96,45 +94,137 @@ trait WebidlParse { fn webidl_parse(&self, program: &mut backend::ast::Program, context: Ctx) -> Result<()>; } -impl WebidlParse<()> for Vec { +fn first_pass<'a>(definitions: &'a [webidl::ast::Definition]) -> FirstPass<'a> { + use webidl::ast::*; + + let mut first_pass = FirstPass::default(); + for def in definitions { + if let Definition::Interface(Interface::NonPartial(NonPartialInterface { name, .. })) = def + { + if first_pass.interfaces.insert(name.clone()) { + warn!("Encountered multiple declarations of {}", name); + } + } + if let Definition::Dictionary(Dictionary::NonPartial(NonPartialDictionary { + name, .. + })) = def + { + if first_pass.dictionaries.insert(name.clone()) { + warn!("Encountered multiple declarations of {}", name); + } + } + if let Definition::Enum(Enum { name, .. }) = def { + if first_pass.enums.insert(name.clone()) { + warn!("Encountered multiple declarations of {}", name); + } + } + if let Definition::Mixin(mixin) = def { + match mixin { + Mixin::NonPartial(mixin) => { + let entry = first_pass + .mixins + .entry(mixin.name.clone()) + .or_insert(Default::default()); + if mem::replace(&mut entry.non_partial, Some(mixin)).is_some() { + warn!( + "Encounterd multiple declarations of {}, using last encountered", + mixin.name + ); + } + } + Mixin::Partial(mixin) => { + let entry = first_pass + .mixins + .entry(mixin.name.clone()) + .or_insert(Default::default()); + entry.partials.push(mixin); + } + } + } + } + first_pass +} + +impl WebidlParse<()> for [webidl::ast::Definition] { fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> { + let first_pass = first_pass(self); for def in self { - def.webidl_parse(program, ())?; + def.webidl_parse(program, &first_pass)?; } Ok(()) } } -impl WebidlParse<()> for webidl::ast::Definition { - fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> { - match *self { - webidl::ast::Definition::Interface(ref interface) => { - interface.webidl_parse(program, ()) +impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Definition { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &'a FirstPass<'b>, + ) -> Result<()> { + match self { + webidl::ast::Definition::Enum(enumeration) => enumeration.webidl_parse(program, ())?, + webidl::ast::Definition::Includes(includes) => { + includes.webidl_parse(program, first_pass)? } - webidl::ast::Definition::Typedef(ref typedef) => typedef.webidl_parse(program, ()), - webidl::ast::Definition::Enum(ref enumeration) => enumeration.webidl_parse(program, ()), + webidl::ast::Definition::Interface(interface) => { + interface.webidl_parse(program, first_pass)? + } + webidl::ast::Definition::Typedef(typedef) => typedef.webidl_parse(program, first_pass)?, // TODO webidl::ast::Definition::Callback(..) | webidl::ast::Definition::Dictionary(..) | webidl::ast::Definition::Implements(..) - | webidl::ast::Definition::Includes(..) - | webidl::ast::Definition::Mixin(..) | webidl::ast::Definition::Namespace(..) => { - warn!("Unsupported WebIDL definition: {:?}", self); - Ok(()) + warn!("Unsupported WebIDL definition: {:?}", self) + } + webidl::ast::Definition::Mixin(_) => { + // handled in the first pass } } + Ok(()) } } -impl WebidlParse<()> for webidl::ast::Interface { - fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> { - match *self { - webidl::ast::Interface::NonPartial(ref interface) => { - interface.webidl_parse(program, ()) +impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Includes { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &'a FirstPass<'b>, + ) -> 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, (&self.includer, first_pass))?; + } + } + for partial in &mixin.partials { + for member in &partial.members { + member.webidl_parse(program, (&self.includer, first_pass))?; + } + } + } + None => warn!("Tried to include missing mixin {}", self.includee), + } + Ok(()) + } +} + +impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Interface { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &'a FirstPass<'b>, + ) -> 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(..) | webidl::ast::Interface::Partial(..) => { + webidl::ast::Interface::Callback(..) => { warn!("Unsupported WebIDL interface: {:?}", self); Ok(()) } @@ -142,14 +232,18 @@ impl WebidlParse<()> for webidl::ast::Interface { } } -impl WebidlParse<()> for webidl::ast::Typedef { - fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> { +impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Typedef { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &'a FirstPass, + ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); } - let dest = rust_ident(&self.name); - let src = match webidl_ty_to_syn_ty(&self.type_, TypePosition::Return) { + let dest = rust_ident(self.name.to_camel_case().as_str()); + let src = match first_pass.webidl_ty_to_syn_ty(&self.type_, TypePosition::Return) { Some(src) => src, None => { warn!( @@ -161,9 +255,7 @@ impl WebidlParse<()> for webidl::ast::Typedef { }; program.type_aliases.push(backend::ast::TypeAlias { - vis: syn::Visibility::Public(syn::VisPublic { - pub_token: Default::default(), - }), + vis: public(), dest, src, }); @@ -172,8 +264,12 @@ impl WebidlParse<()> for webidl::ast::Typedef { } } -impl WebidlParse<()> for webidl::ast::NonPartialInterface { - fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> { +impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::NonPartialInterface { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &'a FirstPass<'b>, + ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); } @@ -183,34 +279,59 @@ impl WebidlParse<()> for webidl::ast::NonPartialInterface { version: None, js_namespace: None, kind: backend::ast::ImportKind::Type(backend::ast::ImportType { - vis: syn::Visibility::Public(syn::VisPublic { - pub_token: Default::default(), - }), + vis: public(), name: rust_ident(&self.name), attrs: Vec::new(), }), }); for extended_attribute in &self.extended_attributes { - extended_attribute.webidl_parse(program, self)?; + extended_attribute.webidl_parse(program, (self, first_pass))?; } for member in &self.members { - member.webidl_parse(program, &self.name)?; + member.webidl_parse(program, (&self.name, first_pass))?; } Ok(()) } } -impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::ExtendedAttribute { +impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::PartialInterface { fn webidl_parse( &self, program: &mut backend::ast::Program, - interface: &'a webidl::ast::NonPartialInterface, + first_pass: &'a FirstPass<'b>, + ) -> Result<()> { + if util::is_chrome_only(&self.extended_attributes) { + return Ok(()); + } + + if !first_pass.interfaces.contains(&self.name) { + warn!( + "Partial interface {} missing non-partial interface", + self.name + ); + } + + for member in &self.members { + member.webidl_parse(program, (&self.name, first_pass))?; + } + + Ok(()) + } +} + +impl<'a, 'b, 'c> WebidlParse<(&'a webidl::ast::NonPartialInterface, &'b FirstPass<'c>)> + for webidl::ast::ExtendedAttribute +{ + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + (interface, first_pass): (&'a webidl::ast::NonPartialInterface, &'b FirstPass<'c>), ) -> Result<()> { let mut add_constructor = |arguments: &[webidl::ast::Argument], class: &str| { - let self_ty = ident_ty(rust_ident(&interface.name)); + let self_ty = ident_ty(rust_ident(interface.name.to_camel_case().as_str())); let kind = backend::ast::ImportFunctionKind::Method { class: class.to_string(), @@ -234,37 +355,32 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte // > exception**. let throws = true; - create_function( - "new", - arguments - .iter() - .map(|arg| (&*arg.name, &*arg.type_, arg.variadic)), - Some(self_ty), - kind, - structural, - throws, - ).map(|function| { - program.imports.push(backend::ast::Import { - module: None, - version: None, - js_namespace: None, - kind: backend::ast::ImportKind::Function(function), - }) - }) + first_pass + .create_function( + "new", + arguments + .iter() + .map(|arg| (&*arg.name, &*arg.type_, arg.variadic)), + Some(self_ty), + kind, + structural, + throws, + ) + .map(wrap_import_function) + .map(|import| program.imports.push(import)); }; match self { webidl::ast::ExtendedAttribute::ArgumentList( webidl::ast::ArgumentListExtendedAttribute { arguments, name }, - ) - if name == "Constructor" => + ) if name == "Constructor" => { - add_constructor(arguments, &interface.name); + add_constructor(arguments, &interface.name) } webidl::ast::ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) if name == "Constructor" => { - add_constructor(&[], &interface.name); + add_constructor(&[], &interface.name) } webidl::ast::ExtendedAttribute::NamedArgumentList( webidl::ast::NamedArgumentListExtendedAttribute { @@ -272,10 +388,9 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte rhs_arguments, rhs_name, }, - ) - if lhs_name == "NamedConstructor" => + ) if lhs_name == "NamedConstructor" => { - add_constructor(rhs_arguments, rhs_name); + add_constructor(rhs_arguments, rhs_name) } webidl::ast::ExtendedAttribute::ArgumentList(_) | webidl::ast::ExtendedAttribute::Identifier(_) @@ -290,13 +405,15 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte } } -impl<'a> WebidlParse<&'a str> for webidl::ast::InterfaceMember { - fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> { - match *self { - webidl::ast::InterfaceMember::Attribute(ref attr) => { - attr.webidl_parse(program, self_name) - } - webidl::ast::InterfaceMember::Operation(ref op) => op.webidl_parse(program, self_name), +impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::InterfaceMember { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + context: (&'a str, &'b FirstPass<'c>), + ) -> Result<()> { + match self { + webidl::ast::InterfaceMember::Attribute(attr) => attr.webidl_parse(program, context), + webidl::ast::InterfaceMember::Operation(op) => op.webidl_parse(program, context), // TODO webidl::ast::InterfaceMember::Const(_) | webidl::ast::InterfaceMember::Iterable(_) @@ -309,11 +426,32 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::InterfaceMember { } } -impl<'a> WebidlParse<&'a str> for webidl::ast::Attribute { - fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> { +impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::MixinMember { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + context: (&'a str, &'b FirstPass<'c>), + ) -> Result<()> { match self { - webidl::ast::Attribute::Regular(attr) => attr.webidl_parse(program, self_name), - webidl::ast::Attribute::Static(attr) => attr.webidl_parse(program, self_name), + webidl::ast::MixinMember::Attribute(attr) => attr.webidl_parse(program, context), + webidl::ast::MixinMember::Operation(op) => op.webidl_parse(program, context), + // TODO + webidl::ast::MixinMember::Const(_) => { + warn!("Unsupported WebIDL interface member: {:?}", self); + Ok(()) + } + } + } +} +impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Attribute { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + context: (&'a str, &'b FirstPass<'c>), + ) -> Result<()> { + match self { + webidl::ast::Attribute::Regular(attr) => attr.webidl_parse(program, context), + webidl::ast::Attribute::Static(attr) => attr.webidl_parse(program, context), // TODO webidl::ast::Attribute::Stringifier(_) => { warn!("Unsupported WebIDL attribute: {:?}", self); @@ -323,11 +461,15 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::Attribute { } } -impl<'a> WebidlParse<&'a str> for webidl::ast::Operation { - fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> { +impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Operation { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + context: (&'a str, &'b FirstPass<'c>), + ) -> Result<()> { match self { - webidl::ast::Operation::Regular(op) => op.webidl_parse(program, self_name), - webidl::ast::Operation::Static(op) => op.webidl_parse(program, self_name), + webidl::ast::Operation::Regular(op) => op.webidl_parse(program, context), + webidl::ast::Operation::Static(op) => op.webidl_parse(program, context), // TODO webidl::ast::Operation::Special(_) | webidl::ast::Operation::Stringifier(_) => { warn!("Unsupported WebIDL operation: {:?}", self); @@ -337,8 +479,12 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::Operation { } } -impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute { - fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> { +impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::RegularAttribute { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + (self_name, first_pass): (&'a str, &'b FirstPass<'c>), + ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); } @@ -346,25 +492,29 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute { let is_structural = util::is_structural(&self.extended_attributes); let throws = util::throws(&self.extended_attributes); - create_getter( - &self.name, - &self.type_, - self_name, - false, - is_structural, - throws, - ).map(wrap_import_function) - .map(|import| program.imports.push(import)); - - if !self.read_only { - create_setter( + first_pass + .create_getter( &self.name, &self.type_, self_name, false, is_structural, throws, - ).map(wrap_import_function) + ) + .map(wrap_import_function) + .map(|import| program.imports.push(import)); + + if !self.read_only { + first_pass + .create_setter( + &self.name, + &self.type_, + self_name, + false, + is_structural, + throws, + ) + .map(wrap_import_function) .map(|import| program.imports.push(import)); } @@ -372,8 +522,12 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute { } } -impl<'a> WebidlParse<&'a str> for webidl::ast::StaticAttribute { - fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> { +impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::StaticAttribute { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + (self_name, first_pass): (&'a str, &'b FirstPass<'c>), + ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); } @@ -381,25 +535,29 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticAttribute { let is_structural = util::is_structural(&self.extended_attributes); let throws = util::throws(&self.extended_attributes); - create_getter( - &self.name, - &self.type_, - self_name, - true, - is_structural, - throws, - ).map(wrap_import_function) - .map(|import| program.imports.push(import)); - - if !self.read_only { - create_setter( + first_pass + .create_getter( &self.name, &self.type_, self_name, true, is_structural, throws, - ).map(wrap_import_function) + ) + .map(wrap_import_function) + .map(|import| program.imports.push(import)); + + if !self.read_only { + first_pass + .create_setter( + &self.name, + &self.type_, + self_name, + true, + is_structural, + throws, + ) + .map(wrap_import_function) .map(|import| program.imports.push(import)); } @@ -407,44 +565,56 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticAttribute { } } -impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation { - fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> { +impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::RegularOperation { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + (self_name, first_pass): (&'a str, &'b FirstPass<'c>), + ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); } let throws = util::throws(&self.extended_attributes); - create_basic_method( - &self.arguments, - self.name.as_ref(), - &self.return_type, - self_name, - false, - throws, - ).map(wrap_import_function) + first_pass + .create_basic_method( + &self.arguments, + self.name.as_ref(), + &self.return_type, + 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, self_name: &'a str) -> Result<()> { +impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::StaticOperation { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + (self_name, first_pass): (&'a str, &'b FirstPass<'c>), + ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); } let throws = util::throws(&self.extended_attributes); - create_basic_method( - &self.arguments, - self.name.as_ref(), - &self.return_type, - self_name, - true, - throws, - ).map(wrap_import_function) + first_pass + .create_basic_method( + &self.arguments, + self.name.as_ref(), + &self.return_type, + self_name, + true, + throws, + ) + .map(wrap_import_function) .map(|import| program.imports.push(import)); Ok(()) @@ -458,9 +628,7 @@ impl<'a> WebidlParse<()> for webidl::ast::Enum { version: None, js_namespace: None, kind: backend::ast::ImportKind::Enum(backend::ast::ImportEnum { - vis: syn::Visibility::Public(syn::VisPublic { - pub_token: Default::default(), - }), + vis: public(), name: rust_ident(self.name.to_camel_case().as_str()), variants: self .variants diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index b8edae82e..01263a3f8 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1,8 +1,9 @@ +use std::collections::{BTreeMap, BTreeSet}; use std::iter::{self, FromIterator}; use backend; use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident, simple_path_ty}; -use heck::SnakeCase; +use heck::{CamelCase, SnakeCase}; use proc_macro2::Ident; use syn; use webidl; @@ -17,79 +18,6 @@ fn shared_ref(ty: syn::Type) -> syn::Type { }.into() } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum TypePosition { - Argument, - Return, -} - -pub fn webidl_ty_to_syn_ty(ty: &webidl::ast::Type, pos: TypePosition) -> Option { - // nullable types are not yet supported (see issue #14) - if ty.nullable { - return None; - } - Some(match ty.kind { - // `any` becomes `::wasm_bindgen::JsValue`. - webidl::ast::TypeKind::Any => { - simple_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) => ident_ty(rust_ident(id)), - - // 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")), - - // `DOMString -> `&str` for arguments - webidl::ast::TypeKind::DOMString if pos == TypePosition::Argument => { - shared_ref(ident_ty(raw_ident("str"))) - } - // `DOMString` is not supported yet in other positions. - webidl::ast::TypeKind::DOMString => return None, - - // Support for these types is not yet implemented, so skip - // generating any bindings for this function. - webidl::ast::TypeKind::ArrayBuffer - | webidl::ast::TypeKind::ByteString - | webidl::ast::TypeKind::DataView - | webidl::ast::TypeKind::Error - | webidl::ast::TypeKind::Float32Array - | webidl::ast::TypeKind::Float64Array - | webidl::ast::TypeKind::FrozenArray(_) - | webidl::ast::TypeKind::Int16Array - | webidl::ast::TypeKind::Int32Array - | webidl::ast::TypeKind::Int8Array - | webidl::ast::TypeKind::Object - | webidl::ast::TypeKind::Promise(_) - | webidl::ast::TypeKind::Record(..) - | webidl::ast::TypeKind::Sequence(_) - | webidl::ast::TypeKind::Symbol - | webidl::ast::TypeKind::USVString - | webidl::ast::TypeKind::Uint16Array - | webidl::ast::TypeKind::Uint32Array - | webidl::ast::TypeKind::Uint8Array - | webidl::ast::TypeKind::Uint8ClampedArray - | webidl::ast::TypeKind::Union(_) => { - return None; - } - }) -} - fn simple_fn_arg(ident: Ident, ty: syn::Type) -> syn::ArgCaptured { syn::ArgCaptured { pat: syn::Pat::Ident(syn::PatIdent { @@ -103,49 +31,6 @@ fn simple_fn_arg(ident: Ident, ty: syn::Type) -> syn::ArgCaptured { } } -fn webidl_arguments_to_syn_arg_captured<'a, I>( - arguments: I, - 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: - backend::ast::MethodKind::Operation(backend::ast::Operation { - is_static: false, .. - }), - .. - } = kind - { - let mut res = Vec::with_capacity(len + 1); - res.push(simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone()))); - res - } else { - Vec::with_capacity(len) - }; - - for (name, ty, variadic) in arguments { - if variadic { - warn!("Variadic arguments are not supported yet",); - return None; - } - - match webidl_ty_to_syn_ty(ty, TypePosition::Argument) { - None => { - warn!("Argument's type is not yet supported: {:?}", ty); - return None; - } - Some(ty) => res.push(simple_fn_arg(rust_ident(&name.to_snake_case()), ty)), - } - } - - Some(res) -} - fn unit_ty() -> syn::Type { syn::Type::Tuple(syn::TypeTuple { paren_token: Default::default(), @@ -173,192 +58,344 @@ fn result_ty(t: syn::Type) -> syn::Type { ty.into() } -pub fn create_function<'a, I>( - name: &str, - arguments: I, - mut ret: Option, - kind: backend::ast::ImportFunctionKind, - structural: bool, - catch: bool, -) -> Option -where - I: Iterator, -{ - let rust_name = rust_ident(&name.to_snake_case()); - let name = raw_ident(name); - - let arguments = webidl_arguments_to_syn_arg_captured(arguments, &kind)?; - - let js_ret = ret.clone(); - - if catch { - ret = Some(ret.map_or_else(|| result_ty(unit_ty()), |ret| result_ty(ret))) - } - - let shim = { - let ns = match kind { - backend::ast::ImportFunctionKind::Normal => "", - backend::ast::ImportFunctionKind::Method { ref class, .. } => class, - }; - - raw_ident(&format!("__widl_f_{}_{}", rust_name, ns)) - }; - - Some(backend::ast::ImportFunction { - function: backend::ast::Function { - name, - arguments, - ret, - rust_attrs: vec![], - rust_vis: syn::Visibility::Public(syn::VisPublic { - pub_token: Default::default(), - }), - }, - rust_name, - js_ret, - catch, - structural, - kind, - shim, - }) +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum TypePosition { + Argument, + Return, } -pub fn create_basic_method( - arguments: &[webidl::ast::Argument], - name: Option<&String>, - return_type: &webidl::ast::ReturnType, - self_name: &str, - is_static: bool, - catch: bool, -) -> Option { - let name = match name { - None => { - warn!("Operations without a name are unsupported"); +#[derive(Default)] +pub struct FirstPass<'a> { + pub interfaces: BTreeSet, + pub dictionaries: BTreeSet, + pub enums: BTreeSet, + pub mixins: BTreeMap>, +} + +#[derive(Default)] +pub struct MixinData<'a> { + pub non_partial: Option<&'a webidl::ast::NonPartialMixin>, + pub partials: Vec<&'a webidl::ast::PartialMixin>, +} + +impl<'a> FirstPass<'a> { + pub fn webidl_ty_to_syn_ty( + &self, + ty: &webidl::ast::Type, + pos: TypePosition, + ) -> Option { + // nullable types are not yet supported (see issue #14) + if ty.nullable { return None; } - Some(ref name) => name, - }; + Some(match ty.kind { + // `any` becomes `::wasm_bindgen::JsValue`. + webidl::ast::TypeKind::Any => { + simple_path_ty(vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]) + } - let kind = backend::ast::ImportFunctionKind::Method { - class: self_name.to_string(), - ty: ident_ty(rust_ident(self_name)), - kind: backend::ast::MethodKind::Operation(backend::ast::Operation { - is_static, - kind: backend::ast::OperationKind::Regular, - }), - }; + // 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(id.to_camel_case().as_str())); + if self.interfaces.contains(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 + } + } - let ret = match return_type { - webidl::ast::ReturnType::Void => None, - webidl::ast::ReturnType::NonVoid(ty) => match webidl_ty_to_syn_ty(ty, TypePosition::Return) + // 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")), + + // `DOMString -> `&str` for arguments + webidl::ast::TypeKind::DOMString if pos == TypePosition::Argument => { + shared_ref(ident_ty(raw_ident("str"))) + } + // `DOMString` is not supported yet in other positions. + webidl::ast::TypeKind::DOMString => return None, + + // Support for these types is not yet implemented, so skip + // generating any bindings for this function. + webidl::ast::TypeKind::ArrayBuffer + | webidl::ast::TypeKind::ByteString + | webidl::ast::TypeKind::DataView + | webidl::ast::TypeKind::Error + | webidl::ast::TypeKind::Float32Array + | webidl::ast::TypeKind::Float64Array + | webidl::ast::TypeKind::FrozenArray(_) + | webidl::ast::TypeKind::Int16Array + | webidl::ast::TypeKind::Int32Array + | webidl::ast::TypeKind::Int8Array + | webidl::ast::TypeKind::Object + | webidl::ast::TypeKind::Promise(_) + | webidl::ast::TypeKind::Record(..) + | webidl::ast::TypeKind::Sequence(_) + | webidl::ast::TypeKind::Symbol + | webidl::ast::TypeKind::USVString + | webidl::ast::TypeKind::Uint16Array + | webidl::ast::TypeKind::Uint32Array + | webidl::ast::TypeKind::Uint8Array + | webidl::ast::TypeKind::Uint8ClampedArray + | webidl::ast::TypeKind::Union(_) => { + return None; + } + }) + } + + fn webidl_arguments_to_syn_arg_captured<'b, I>( + &self, + arguments: I, + 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: + backend::ast::MethodKind::Operation(backend::ast::Operation { + is_static: false, .. + }), + .. + } = kind { + let mut res = Vec::with_capacity(len + 1); + res.push(simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone()))); + res + } else { + Vec::with_capacity(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) { + None => { + warn!("Argument's type is not yet supported: {:?}", ty); + return None; + } + Some(ty) => res.push(simple_fn_arg(rust_ident(&name.to_snake_case()), ty)), + } + } + + Some(res) + } + + pub fn create_function<'b, I>( + &self, + name: &str, + arguments: I, + mut ret: Option, + kind: backend::ast::ImportFunctionKind, + structural: bool, + catch: bool, + ) -> Option + where + I: Iterator, + { + let rust_name = rust_ident(&name.to_snake_case()); + let name = raw_ident(name); + + let arguments = self.webidl_arguments_to_syn_arg_captured(arguments, &kind)?; + + let js_ret = ret.clone(); + + if catch { + ret = Some(ret.map_or_else(|| result_ty(unit_ty()), |ret| result_ty(ret))) + } + + let shim = { + let ns = match kind { + backend::ast::ImportFunctionKind::Normal => "", + backend::ast::ImportFunctionKind::Method { ref class, .. } => class, + }; + + raw_ident(&format!("__widl_f_{}_{}", rust_name, ns)) + }; + + Some(backend::ast::ImportFunction { + function: backend::ast::Function { + name, + arguments, + ret, + rust_attrs: vec![], + rust_vis: public(), + }, + rust_name, + js_ret, + catch, + structural, + kind, + shim, + }) + } + + pub fn create_basic_method( + &self, + arguments: &[webidl::ast::Argument], + name: Option<&String>, + return_type: &webidl::ast::ReturnType, + self_name: &str, + is_static: bool, + catch: bool, + ) -> Option { + let name = match name { None => { - warn!("Operation's return type is not yet supported: {:?}", ty); + warn!("Operations without a name are unsupported"); + return None; + } + Some(ref name) => name, + }; + + let kind = backend::ast::ImportFunctionKind::Method { + class: self_name.to_string(), + ty: ident_ty(rust_ident(self_name.to_camel_case().as_str())), + kind: backend::ast::MethodKind::Operation(backend::ast::Operation { + is_static, + kind: backend::ast::OperationKind::Regular, + }), + }; + + 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) { + None => { + warn!("Operation's return type is not yet supported: {:?}", ty); + return None; + } + Some(ty) => Some(ty), + } + } + }; + + self.create_function( + &name, + arguments + .iter() + .map(|arg| (&*arg.name, &*arg.type_, arg.variadic)), + ret, + kind, + false, + catch, + ) + } + + pub fn create_getter( + &self, + name: &str, + ty: &webidl::ast::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) { + None => { + warn!("Attribute's type does not yet support reading: {:?}", ty); return None; } Some(ty) => Some(ty), - }, - }; + }; - create_function( - &name, - arguments - .iter() - .map(|arg| (&*arg.name, &*arg.type_, arg.variadic)), - ret, - kind, - false, - catch, - ) -} + let kind = backend::ast::ImportFunctionKind::Method { + class: self_name.to_string(), + ty: ident_ty(rust_ident(self_name.to_camel_case().as_str())), + kind: backend::ast::MethodKind::Operation(backend::ast::Operation { + is_static, + kind: backend::ast::OperationKind::Getter(Some(raw_ident(name))), + }), + }; -pub fn create_getter( - name: &str, - ty: &webidl::ast::Type, - self_name: &str, - is_static: bool, - is_structural: bool, - catch: bool, -) -> Option { - let ret = match webidl_ty_to_syn_ty(ty, TypePosition::Return) { - None => { - warn!("Attribute's type does not yet support reading: {:?}", ty); - return None; - } - Some(ty) => Some(ty), - }; + self.create_function(name, iter::empty(), ret, kind, is_structural, catch) + } - let kind = backend::ast::ImportFunctionKind::Method { - class: self_name.to_string(), - ty: ident_ty(rust_ident(self_name)), - kind: backend::ast::MethodKind::Operation(backend::ast::Operation { - is_static, - kind: backend::ast::OperationKind::Getter(Some(raw_ident(name))), - }), - }; + pub fn create_setter( + &self, + name: &str, + ty: &webidl::ast::Type, + self_name: &str, + is_static: bool, + is_structural: bool, + catch: bool, + ) -> Option { + let kind = backend::ast::ImportFunctionKind::Method { + class: self_name.to_string(), + ty: ident_ty(rust_ident(self_name.to_camel_case().as_str())), + kind: backend::ast::MethodKind::Operation(backend::ast::Operation { + is_static, + kind: backend::ast::OperationKind::Setter(Some(raw_ident(name))), + }), + }; - create_function(name, iter::empty(), ret, kind, is_structural, catch) -} - -pub fn create_setter( - name: &str, - ty: &webidl::ast::Type, - self_name: &str, - is_static: bool, - is_structural: bool, - catch: bool, -) -> Option { - let kind = backend::ast::ImportFunctionKind::Method { - class: self_name.to_string(), - ty: ident_ty(rust_ident(self_name)), - kind: backend::ast::MethodKind::Operation(backend::ast::Operation { - is_static, - kind: backend::ast::OperationKind::Setter(Some(raw_ident(name))), - }), - }; - - create_function( - &format!("set_{}", name), - iter::once((name, ty, false)), - None, - kind, - is_structural, - catch, - ) + self.create_function( + &format!("set_{}", name), + iter::once((name, ty, false)), + None, + kind, + is_structural, + catch, + ) + } } /// ChromeOnly is for things that are only exposed to priveleged code in Firefox. pub fn is_chrome_only(ext_attrs: &[Box]) -> bool { - ext_attrs.iter().any(|external_attribute| { - return match &**external_attribute { - ExtendedAttribute::ArgumentList(al) => al.name == "ChromeOnly", - ExtendedAttribute::Identifier(i) => i.lhs == "ChromeOnly", - ExtendedAttribute::IdentifierList(il) => il.lhs == "ChromeOnly", - ExtendedAttribute::NamedArgumentList(nal) => nal.lhs_name == "ChromeOnly", - ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => { - name == "ChromeOnly" - } - ExtendedAttribute::NoArguments(_na) => false, - }; + ext_attrs.iter().any(|attr| match &**attr { + ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => { + name == "ChromeOnly" + } + _ => false, }) } pub fn is_structural(attrs: &[Box]) -> bool { - attrs.iter().any(|attr| { - if let ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(ref name)) = **attr { + attrs.iter().any(|attr| match &**attr { + ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => { name == "Unforgeable" - } else { - false } + _ => false, }) } pub fn throws(attrs: &[Box]) -> bool { - attrs.iter().any(|attr| { - if let ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(ref name)) = **attr { - name == "Throws" - } else { - false - } + attrs.iter().any(|attr| match &**attr { + ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => name == "Throws", + _ => false, + }) +} + +pub fn public() -> syn::Visibility { + syn::Visibility::Public(syn::VisPublic { + pub_token: Default::default(), }) } diff --git a/crates/webidl/tests/all/simple.rs b/crates/webidl/tests/all/simple.rs index ad177a086..086ef40d7 100644 --- a/crates/webidl/tests/all/simple.rs +++ b/crates/webidl/tests/all/simple.rs @@ -44,14 +44,14 @@ fn method() { let pi = Foo::new(3.14159).unwrap(); let e = Foo::new(2.71828).unwrap(); // TODO: figure out why the following doesn't fail - // assert!(!pi.my_cmp(Foo::new(3.14159).unwrap())); - let tmp = pi.my_cmp(Foo::new(3.14159).unwrap()); + // assert!(!pi.my_cmp(&pi)); + let tmp = pi.my_cmp(&pi); assert!(tmp); - let tmp =!pi.my_cmp(Foo::new(2.71828).unwrap()); + let tmp =!pi.my_cmp(&e); assert!(tmp); - let tmp = !e.my_cmp(Foo::new(3.14159).unwrap()); + let tmp = !e.my_cmp(&pi); assert!(tmp); - let tmp = e.my_cmp(Foo::new(2.71828).unwrap()); + let tmp = e.my_cmp(&e); assert!(tmp); } "#, @@ -370,3 +370,130 @@ fn unforgeable_is_structural() { ) .test(); } + +#[test] +fn partial_interface() { + project() + .file( + "foo.webidl", + r#" + [Constructor] + interface Foo { + readonly attribute short un; + short deux(); + }; + + partial interface Foo { + readonly attribute short trois; + short quatre(); + }; + "#, + ) + .file( + "foo.js", + r#" + export class Foo { + get un() { + return 1; + } + deux() { + return 2; + } + get trois() { + return 3; + } + quatre() { + return 4; + } + } + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(proc_macro, wasm_custom_section, wasm_import_module)] + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + + pub mod foo; + + #[wasm_bindgen] + pub fn test() { + let f = foo::Foo::new().unwrap(); + assert_eq!(f.un(), 1); + assert_eq!(f.deux(), 2); + assert_eq!(f.trois(), 3); + assert_eq!(f.quatre(), 4); + } + "#, + ) + .test(); +} + +#[test] +fn mixin() { + project() + .file( + "foo.webidl", + r#" + [Constructor(short bar)] + interface Foo { + static attribute short defaultBar; + }; + + interface mixin Bar { + readonly attribute short bar; + }; + + partial interface mixin Bar { + void addToBar(short other); + }; + + Foo includes Bar; + "#, + ) + .file( + "foo.js", + r#" + export class Foo { + constructor(bar) { + this._bar = bar | Foo.defaultBar; + } + static get defaultBar() { + return Foo._defaultBar; + } + static set defaultBar(defaultBar) { + Foo._defaultBar = defaultBar; + } + get bar() { + return this._bar; + } + addToBar(other) { + this._bar += other; + } + } + "#, + ) + .file( + "src/lib.rs", + r#" + #![feature(proc_macro, wasm_custom_section, wasm_import_module)] + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + + pub mod foo; + + use foo::Foo; + + #[wasm_bindgen] + pub fn test() { + let f = Foo::new(1).unwrap(); + assert_eq!(f.bar(), 1); + Foo::set_default_bar(7); + f.add_to_bar(Foo::default_bar()); + assert_eq!(f.bar(), 8); + } + "#, + ) + .test(); +} From d5fee8d5d111876951790e65cc5ed25c4991f682 Mon Sep 17 00:00:00 2001 From: "R. Andrew Ohana" Date: Fri, 13 Jul 2018 21:46:36 -0700 Subject: [PATCH 2/3] webidl: move first pass logic to new module I also updated it so that it is modeled in the same extensible way as the WebidlParse trait. --- crates/webidl/src/first_pass.rs | 150 +++++++++++++++++++++ crates/webidl/src/lib.rs | 217 +++++++++++++++--------------- crates/webidl/src/util.rs | 19 +-- crates/webidl/tests/all/simple.rs | 14 +- 4 files changed, 263 insertions(+), 137 deletions(-) create mode 100644 crates/webidl/src/first_pass.rs diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs new file mode 100644 index 000000000..e48cf6289 --- /dev/null +++ b/crates/webidl/src/first_pass.rs @@ -0,0 +1,150 @@ +use std::{ + collections::{BTreeMap, BTreeSet}, mem, +}; + +use webidl; + +use super::Result; + +#[derive(Default)] +pub(crate) struct FirstPassRecord<'a> { + pub(crate) interfaces: BTreeSet, + pub(crate) dictionaries: BTreeSet, + pub(crate) enums: BTreeSet, + pub(crate) mixins: BTreeMap>, +} + +#[derive(Default)] +pub(crate) struct MixinData<'a> { + pub(crate) non_partial: Option<&'a webidl::ast::NonPartialMixin>, + pub(crate) partials: Vec<&'a webidl::ast::PartialMixin>, +} + +pub(crate) trait FirstPass { + fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>) -> Result<()>; +} + +impl FirstPass for [webidl::ast::Definition] { + fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>) -> Result<()> { + for def in self { + def.first_pass(record)?; + } + + Ok(()) + } +} + +impl FirstPass for webidl::ast::Definition { + fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>) -> Result<()> { + use webidl::ast::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), + _ => { + // Other definitions aren't currently used in the first pass + Ok(()) + } + } + } +} + +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 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); + } + + 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(()) + } +} + +impl FirstPass for webidl::ast::Interface { + fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>) -> Result<()> { + use webidl::ast::Interface::*; + + match self { + NonPartial(interface) => interface.first_pass(record), + _ => { + // Other interfaces aren't currently used in the first pass + Ok(()) + } + } + } +} + +impl FirstPass for webidl::ast::NonPartialInterface { + fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>) -> Result<()> { + if record.interfaces.insert(self.name.clone()) { + warn!("Encountered multiple declarations of {}", self.name); + } + + Ok(()) + } +} + +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 + .mixins + .entry(self.name.clone()) + .or_insert(Default::default()); + if mem::replace(&mut entry.non_partial, Some(self)).is_some() { + warn!( + "Encounterd multiple declarations of {}, using last encountered", + self.name + ); + } + + Ok(()) + } +} + +impl FirstPass for webidl::ast::PartialMixin { + fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>) -> Result<()> { + let entry = record + .mixins + .entry(self.name.clone()) + .or_insert(Default::default()); + entry.partials.push(self); + + Ok(()) + } +} diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 511239aa4..02fe8224d 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -18,13 +18,13 @@ extern crate syn; extern crate wasm_bindgen_backend as backend; extern crate webidl; +mod first_pass; mod util; use std::collections::BTreeSet; use std::fs; use std::io::{self, Read}; use std::iter::FromIterator; -use std::mem; use std::path::Path; use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports}; @@ -33,7 +33,8 @@ use failure::ResultExt; use heck::CamelCase; use quote::ToTokens; -use util::{public, FirstPass, TypePosition}; +use first_pass::{FirstPass, FirstPassRecord}; +use util::{public, TypePosition}; /// Either `Ok(t)` or `Err(failure::Error)`. pub type Result = ::std::result::Result; @@ -52,8 +53,10 @@ fn parse_file(webidl_path: &Path) -> Result { fn parse(webidl_source: &str) -> Result { let definitions = webidl::parse_string(webidl_source).context("parsing WebIDL source text")?; - let mut program = backend::ast::Program::default(); - definitions.webidl_parse(&mut program, ())?; + let mut first_pass_record = Default::default(); + definitions.first_pass(&mut first_pass_record)?; + let mut program = Default::default(); + definitions.webidl_parse(&mut program, &first_pass_record, ())?; Ok(program) } @@ -91,85 +94,48 @@ fn compile_ast(mut ast: backend::ast::Program) -> String { } trait WebidlParse { - fn webidl_parse(&self, program: &mut backend::ast::Program, context: Ctx) -> Result<()>; -} - -fn first_pass<'a>(definitions: &'a [webidl::ast::Definition]) -> FirstPass<'a> { - use webidl::ast::*; - - let mut first_pass = FirstPass::default(); - for def in definitions { - if let Definition::Interface(Interface::NonPartial(NonPartialInterface { name, .. })) = def - { - if first_pass.interfaces.insert(name.clone()) { - warn!("Encountered multiple declarations of {}", name); - } - } - if let Definition::Dictionary(Dictionary::NonPartial(NonPartialDictionary { - name, .. - })) = def - { - if first_pass.dictionaries.insert(name.clone()) { - warn!("Encountered multiple declarations of {}", name); - } - } - if let Definition::Enum(Enum { name, .. }) = def { - if first_pass.enums.insert(name.clone()) { - warn!("Encountered multiple declarations of {}", name); - } - } - if let Definition::Mixin(mixin) = def { - match mixin { - Mixin::NonPartial(mixin) => { - let entry = first_pass - .mixins - .entry(mixin.name.clone()) - .or_insert(Default::default()); - if mem::replace(&mut entry.non_partial, Some(mixin)).is_some() { - warn!( - "Encounterd multiple declarations of {}, using last encountered", - mixin.name - ); - } - } - Mixin::Partial(mixin) => { - let entry = first_pass - .mixins - .entry(mixin.name.clone()) - .or_insert(Default::default()); - entry.partials.push(mixin); - } - } - } - } - first_pass + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'_>, + context: Ctx, + ) -> Result<()>; } impl WebidlParse<()> for [webidl::ast::Definition] { - fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> { - let first_pass = first_pass(self); + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'_>, + (): (), + ) -> Result<()> { for def in self { - def.webidl_parse(program, &first_pass)?; + def.webidl_parse(program, first_pass, ())?; } Ok(()) } } -impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Definition { +impl WebidlParse<()> for webidl::ast::Definition { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &'a FirstPass<'b>, + first_pass: &FirstPassRecord<'_>, + (): (), ) -> Result<()> { match self { - webidl::ast::Definition::Enum(enumeration) => enumeration.webidl_parse(program, ())?, + webidl::ast::Definition::Enum(enumeration) => { + enumeration.webidl_parse(program, first_pass, ())? + } webidl::ast::Definition::Includes(includes) => { - includes.webidl_parse(program, first_pass)? + includes.webidl_parse(program, first_pass, ())? } webidl::ast::Definition::Interface(interface) => { - interface.webidl_parse(program, first_pass)? + interface.webidl_parse(program, first_pass, ())? + } + webidl::ast::Definition::Typedef(typedef) => { + typedef.webidl_parse(program, first_pass, ())? } - webidl::ast::Definition::Typedef(typedef) => typedef.webidl_parse(program, first_pass)?, // TODO webidl::ast::Definition::Callback(..) | webidl::ast::Definition::Dictionary(..) @@ -185,22 +151,23 @@ impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Definition { } } -impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Includes { +impl WebidlParse<()> for webidl::ast::Includes { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &'a FirstPass<'b>, + 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, (&self.includer, first_pass))?; + member.webidl_parse(program, first_pass, &self.includer)?; } } for partial in &mixin.partials { for member in &partial.members { - member.webidl_parse(program, (&self.includer, first_pass))?; + member.webidl_parse(program, first_pass, &self.includer)?; } } } @@ -210,18 +177,19 @@ impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Includes { } } -impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Interface { +impl WebidlParse<()> for webidl::ast::Interface { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &'a FirstPass<'b>, + first_pass: &FirstPassRecord<'_>, + (): (), ) -> Result<()> { match self { webidl::ast::Interface::NonPartial(interface) => { - interface.webidl_parse(program, first_pass) + interface.webidl_parse(program, first_pass, ()) } webidl::ast::Interface::Partial(interface) => { - interface.webidl_parse(program, first_pass) + interface.webidl_parse(program, first_pass, ()) } // TODO webidl::ast::Interface::Callback(..) => { @@ -232,11 +200,12 @@ impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Interface { } } -impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Typedef { +impl WebidlParse<()> for webidl::ast::Typedef { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &'a FirstPass, + first_pass: &FirstPassRecord<'_>, + (): (), ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); @@ -264,11 +233,12 @@ impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::Typedef { } } -impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::NonPartialInterface { +impl WebidlParse<()> for webidl::ast::NonPartialInterface { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &'a FirstPass<'b>, + first_pass: &FirstPassRecord<'_>, + (): (), ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); @@ -286,22 +256,23 @@ impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::NonPartialInterface }); for extended_attribute in &self.extended_attributes { - extended_attribute.webidl_parse(program, (self, first_pass))?; + extended_attribute.webidl_parse(program, first_pass, self)?; } for member in &self.members { - member.webidl_parse(program, (&self.name, first_pass))?; + member.webidl_parse(program, first_pass, &self.name)?; } Ok(()) } } -impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::PartialInterface { +impl WebidlParse<()> for webidl::ast::PartialInterface { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &'a FirstPass<'b>, + first_pass: &FirstPassRecord<'_>, + (): (), ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); @@ -315,20 +286,19 @@ impl<'a, 'b> WebidlParse<&'a FirstPass<'b>> for webidl::ast::PartialInterface { } for member in &self.members { - member.webidl_parse(program, (&self.name, first_pass))?; + member.webidl_parse(program, first_pass, &self.name)?; } Ok(()) } } -impl<'a, 'b, 'c> WebidlParse<(&'a webidl::ast::NonPartialInterface, &'b FirstPass<'c>)> - for webidl::ast::ExtendedAttribute -{ +impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::ExtendedAttribute { fn webidl_parse( &self, program: &mut backend::ast::Program, - (interface, first_pass): (&'a webidl::ast::NonPartialInterface, &'b FirstPass<'c>), + first_pass: &FirstPassRecord<'_>, + interface: &'a webidl::ast::NonPartialInterface, ) -> Result<()> { let mut add_constructor = |arguments: &[webidl::ast::Argument], class: &str| { let self_ty = ident_ty(rust_ident(interface.name.to_camel_case().as_str())); @@ -405,15 +375,20 @@ impl<'a, 'b, 'c> WebidlParse<(&'a webidl::ast::NonPartialInterface, &'b FirstPas } } -impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::InterfaceMember { +impl<'a> WebidlParse<&'a str> for webidl::ast::InterfaceMember { fn webidl_parse( &self, program: &mut backend::ast::Program, - context: (&'a str, &'b FirstPass<'c>), + first_pass: &FirstPassRecord<'_>, + self_name: &'a str, ) -> Result<()> { match self { - webidl::ast::InterfaceMember::Attribute(attr) => attr.webidl_parse(program, context), - webidl::ast::InterfaceMember::Operation(op) => op.webidl_parse(program, context), + webidl::ast::InterfaceMember::Attribute(attr) => { + attr.webidl_parse(program, first_pass, self_name) + } + webidl::ast::InterfaceMember::Operation(op) => { + op.webidl_parse(program, first_pass, self_name) + } // TODO webidl::ast::InterfaceMember::Const(_) | webidl::ast::InterfaceMember::Iterable(_) @@ -426,15 +401,20 @@ impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Inte } } -impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::MixinMember { +impl<'a> WebidlParse<&'a str> for webidl::ast::MixinMember { fn webidl_parse( &self, program: &mut backend::ast::Program, - context: (&'a str, &'b FirstPass<'c>), + first_pass: &FirstPassRecord<'_>, + self_name: &'a str, ) -> Result<()> { match self { - webidl::ast::MixinMember::Attribute(attr) => attr.webidl_parse(program, context), - webidl::ast::MixinMember::Operation(op) => op.webidl_parse(program, context), + webidl::ast::MixinMember::Attribute(attr) => { + attr.webidl_parse(program, first_pass, self_name) + } + webidl::ast::MixinMember::Operation(op) => { + op.webidl_parse(program, first_pass, self_name) + } // TODO webidl::ast::MixinMember::Const(_) => { warn!("Unsupported WebIDL interface member: {:?}", self); @@ -443,15 +423,20 @@ impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Mixi } } } -impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Attribute { +impl<'a> WebidlParse<&'a str> for webidl::ast::Attribute { fn webidl_parse( &self, program: &mut backend::ast::Program, - context: (&'a str, &'b FirstPass<'c>), + first_pass: &FirstPassRecord<'_>, + self_name: &'a str, ) -> Result<()> { match self { - webidl::ast::Attribute::Regular(attr) => attr.webidl_parse(program, context), - webidl::ast::Attribute::Static(attr) => attr.webidl_parse(program, context), + 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); @@ -461,15 +446,16 @@ impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Attr } } -impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Operation { +impl<'a> WebidlParse<&'a str> for webidl::ast::Operation { fn webidl_parse( &self, program: &mut backend::ast::Program, - context: (&'a str, &'b FirstPass<'c>), + first_pass: &FirstPassRecord<'_>, + self_name: &'a str, ) -> Result<()> { match self { - webidl::ast::Operation::Regular(op) => op.webidl_parse(program, context), - webidl::ast::Operation::Static(op) => op.webidl_parse(program, context), + 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); @@ -479,11 +465,12 @@ impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Oper } } -impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::RegularAttribute { +impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute { fn webidl_parse( &self, program: &mut backend::ast::Program, - (self_name, first_pass): (&'a str, &'b FirstPass<'c>), + first_pass: &FirstPassRecord<'_>, + self_name: &'a str, ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); @@ -522,11 +509,12 @@ impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Regu } } -impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::StaticAttribute { +impl<'a> WebidlParse<&'a str> for webidl::ast::StaticAttribute { fn webidl_parse( &self, program: &mut backend::ast::Program, - (self_name, first_pass): (&'a str, &'b FirstPass<'c>), + first_pass: &FirstPassRecord<'_>, + self_name: &'a str, ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); @@ -565,11 +553,12 @@ impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Stat } } -impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::RegularOperation { +impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation { fn webidl_parse( &self, program: &mut backend::ast::Program, - (self_name, first_pass): (&'a str, &'b FirstPass<'c>), + first_pass: &FirstPassRecord<'_>, + self_name: &'a str, ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); @@ -593,11 +582,12 @@ impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Regu } } -impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::StaticOperation { +impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation { fn webidl_parse( &self, program: &mut backend::ast::Program, - (self_name, first_pass): (&'a str, &'b FirstPass<'c>), + first_pass: &FirstPassRecord<'_>, + self_name: &'a str, ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); @@ -622,7 +612,12 @@ impl<'a, 'b, 'c> WebidlParse<(&'a str, &'b FirstPass<'c>)> for webidl::ast::Stat } impl<'a> WebidlParse<()> for webidl::ast::Enum { - fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + _: &FirstPassRecord<'_>, + (): (), + ) -> Result<()> { program.imports.push(backend::ast::Import { module: None, version: None, diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 01263a3f8..2eec7b07b 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1,4 +1,3 @@ -use std::collections::{BTreeMap, BTreeSet}; use std::iter::{self, FromIterator}; use backend; @@ -9,6 +8,8 @@ use syn; use webidl; use webidl::ast::ExtendedAttribute; +use first_pass::FirstPassRecord; + fn shared_ref(ty: syn::Type) -> syn::Type { syn::TypeReference { and_token: Default::default(), @@ -64,21 +65,7 @@ pub enum TypePosition { Return, } -#[derive(Default)] -pub struct FirstPass<'a> { - pub interfaces: BTreeSet, - pub dictionaries: BTreeSet, - pub enums: BTreeSet, - pub mixins: BTreeMap>, -} - -#[derive(Default)] -pub struct MixinData<'a> { - pub non_partial: Option<&'a webidl::ast::NonPartialMixin>, - pub partials: Vec<&'a webidl::ast::PartialMixin>, -} - -impl<'a> FirstPass<'a> { +impl<'a> FirstPassRecord<'a> { pub fn webidl_ty_to_syn_ty( &self, ty: &webidl::ast::Type, diff --git a/crates/webidl/tests/all/simple.rs b/crates/webidl/tests/all/simple.rs index 086ef40d7..3af571855 100644 --- a/crates/webidl/tests/all/simple.rs +++ b/crates/webidl/tests/all/simple.rs @@ -43,16 +43,10 @@ fn method() { pub fn test() { let pi = Foo::new(3.14159).unwrap(); let e = Foo::new(2.71828).unwrap(); - // TODO: figure out why the following doesn't fail - // assert!(!pi.my_cmp(&pi)); - let tmp = pi.my_cmp(&pi); - assert!(tmp); - let tmp =!pi.my_cmp(&e); - assert!(tmp); - let tmp = !e.my_cmp(&pi); - assert!(tmp); - let tmp = e.my_cmp(&e); - assert!(tmp); + assert!(pi.my_cmp(&pi)); + assert!(!pi.my_cmp(&e)); + assert!(!e.my_cmp(&pi)); + assert!(e.my_cmp(&e)); } "#, ) From 80384d8da917a1e70ca283eb226a3be5f6786bc4 Mon Sep 17 00:00:00 2001 From: "R. Andrew Ohana" Date: Fri, 13 Jul 2018 22:36:51 -0700 Subject: [PATCH 3/3] address my comments for #470 --- crates/backend/src/ast.rs | 12 ++---------- crates/backend/src/codegen.rs | 27 +++++++++++++++++---------- crates/backend/src/defined.rs | 3 ++- crates/cli-support/src/js/mod.rs | 9 ++------- crates/shared/src/lib.rs | 4 ---- crates/webidl/src/lib.rs | 25 ++++++++++--------------- 6 files changed, 33 insertions(+), 47 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index cfdc23675..5a332a087 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -10,6 +10,7 @@ pub struct Program { pub enums: Vec, pub structs: Vec, pub type_aliases: Vec, + pub consts: Vec, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] @@ -42,7 +43,6 @@ pub enum ImportKind { Static(ImportStatic), Type(ImportType), Enum(ImportEnum), - Const(Const), } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] @@ -179,7 +179,7 @@ pub struct TypeAlias { pub struct Const { pub vis: syn::Visibility, pub name: Ident, - pub interface_name: Ident, + pub class: Option, pub ty: syn::Type, pub value: ConstValue, } @@ -312,7 +312,6 @@ impl ImportKind { ImportKind::Static(_) => false, ImportKind::Type(_) => false, ImportKind::Enum(_) => false, - ImportKind::Const(_) => false, } } @@ -322,7 +321,6 @@ impl ImportKind { ImportKind::Static(ref f) => shared::ImportKind::Static(f.shared()), ImportKind::Type(ref f) => shared::ImportKind::Type(f.shared()), ImportKind::Enum(ref f) => shared::ImportKind::Enum(f.shared()), - ImportKind::Const(ref f) => shared::ImportKind::Const(f.shared()), } } } @@ -425,9 +423,3 @@ impl StructField { } } } - -impl Const { - fn shared(&self) -> shared::Const { - shared::Const {} - } -} diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index a83d19239..365345c36 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -62,6 +62,9 @@ impl ToTokens for ast::Program { for a in self.type_aliases.iter() { a.to_tokens(tokens); } + for c in self.consts.iter() { + c.to_tokens(tokens); + } // Generate a static which will eventually be what lives in a custom section // of the wasm executable. For now it's just a plain old static, but we'll @@ -501,7 +504,6 @@ impl ToTokens for ast::ImportKind { ast::ImportKind::Static(ref s) => s.to_tokens(tokens), ast::ImportKind::Type(ref t) => t.to_tokens(tokens), ast::ImportKind::Enum(ref e) => e.to_tokens(tokens), - ast::ImportKind::Const(ref c) => c.to_tokens(tokens), } } } @@ -843,7 +845,6 @@ impl<'a> ToTokens for DescribeImport<'a> { ast::ImportKind::Static(_) => return, ast::ImportKind::Type(_) => return, ast::ImportKind::Enum(_) => return, - ast::ImportKind::Const(_) => return, }; let describe_name = format!("__wbindgen_describe_{}", f.shim); let describe_name = Ident::new(&describe_name, Span::call_site()); @@ -967,7 +968,6 @@ impl ToTokens for ast::Const { let vis = &self.vis; let name = &self.name; - let interface_name = &self.interface_name; let ty = &self.ty; let value: TokenStream = match self.value { @@ -984,17 +984,24 @@ impl ToTokens for ast::Const { FloatLiteral(f) => { let f = Literal::f64_unsuffixed(f); quote!(#f) - }, + } IntegerLiteral(i) => { let i = Literal::i64_unsuffixed(i); quote!(#i) - }, + } Null => unimplemented!(), }; - (quote! { - impl #interface_name { - #vis const #name: #ty = #value; - } - }).to_tokens(tokens); + + let declaration = quote!(#vis const #name: #ty = #value;); + + if let Some(class) = &self.class { + (quote! { + impl #class { + #declaration + } + }).to_tokens(tokens); + } else { + declaration.to_tokens(tokens); + } } } diff --git a/crates/backend/src/defined.rs b/crates/backend/src/defined.rs index 84eabe505..9b5035554 100644 --- a/crates/backend/src/defined.rs +++ b/crates/backend/src/defined.rs @@ -70,6 +70,7 @@ impl ImportedTypes for ast::Program { { self.imports.imported_types(f); self.type_aliases.imported_types(f); + self.consts.imported_types(f); } } @@ -106,7 +107,6 @@ impl ImportedTypes for ast::ImportKind { ast::ImportKind::Function(fun) => fun.imported_types(f), ast::ImportKind::Type(ty) => ty.imported_types(f), ast::ImportKind::Enum(enm) => enm.imported_types(f), - ast::ImportKind::Const(c) => c.imported_types(f), } } } @@ -254,6 +254,7 @@ impl RemoveUndefinedImports for ast::Program { { self.imports.remove_undefined_imports(is_defined); self.type_aliases.remove_undefined_imports(is_defined); + self.consts.remove_undefined_imports(is_defined); } } diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 9a9a7271a..84e2f0b24 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1758,7 +1758,6 @@ impl<'a, 'b> SubContext<'a, 'b> { } shared::ImportKind::Type(_) => {} shared::ImportKind::Enum(_) => {} - shared::ImportKind::Const(_) => {} } Ok(()) } @@ -1918,9 +1917,7 @@ impl<'a, 'b> SubContext<'a, 'b> { " const {}_target = {} {} ; ", - import.shim, - target, - fallback + import.shim, target, fallback )); format!( "{}_target{}", @@ -2020,9 +2017,7 @@ fn format_doc_comments(comments: &Vec, js_doc_comments: Option) .map(|c| format!("*{}\n", c.trim_matches('"'))) .collect(); let doc = if let Some(docs) = js_doc_comments { - docs.lines() - .map(|l| format!("* {} \n", l)) - .collect() + docs.lines().map(|l| format!("* {} \n", l)).collect() } else { String::new() }; diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 8985733dc..8df1e5344 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -34,7 +34,6 @@ pub enum ImportKind { Static(ImportStatic), Type(ImportType), Enum(ImportEnum), - Const(Const) } #[derive(Deserialize, Serialize)] @@ -125,9 +124,6 @@ pub struct StructField { pub comments: Vec, } -#[derive(Deserialize, Serialize)] -pub struct Const {} - pub fn new_function(struct_name: &str) -> String { let mut name = format!("__wbg_"); name.extend(struct_name.chars().flat_map(|s| s.to_lowercase())); diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 37c187582..d4b819864 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -647,23 +647,18 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::Const { &self, program: &mut backend::ast::Program, _: &FirstPassRecord<'_>, - interface_name: &'a str, + self_name: &'a str, ) -> Result<()> { - let syn_ty = webidl_const_ty_to_syn_ty(&self.type_); - program.imports.push(backend::ast::Import { - module: None, - version: None, - js_namespace: None, - kind: backend::ast::ImportKind::Const(backend::ast::Const { - vis: syn::Visibility::Public(syn::VisPublic { - pub_token: Default::default(), - }), - name: rust_ident(self.name.to_shouty_snake_case().as_str()), - interface_name: rust_ident(interface_name.to_camel_case().as_str()), - ty: syn_ty, - value: webidl_const_v_to_backend_const_v(&self.value), - }), + let ty = webidl_const_ty_to_syn_ty(&self.type_); + + program.consts.push(backend::ast::Const { + vis: public(), + name: rust_ident(self.name.to_shouty_snake_case().as_str()), + class: Some(rust_ident(self_name.to_camel_case().as_str())), + ty, + value: webidl_const_v_to_backend_const_v(&self.value), }); + Ok(()) } }