diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index cfdc2367..5a332a08 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 8813075a..c8261b46 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 @@ -502,7 +505,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), } } } @@ -845,7 +847,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()); @@ -969,7 +970,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 { @@ -986,17 +986,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 84eabe50..9b503555 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 9a9a7271..84e2f0b2 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 8985733d..8df1e534 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/first_pass.rs b/crates/webidl/src/first_pass.rs new file mode 100644 index 00000000..e48cf628 --- /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 13b51e9f..d4b81986 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -18,6 +18,7 @@ extern crate syn; extern crate wasm_bindgen_backend as backend; extern crate webidl; +mod first_pass; mod util; use std::collections::BTreeSet; @@ -32,10 +33,8 @@ use failure::ResultExt; use heck::{CamelCase, ShoutySnakeCase}; use quote::ToTokens; -use util::{ - create_basic_method, create_function, create_getter, create_setter, webidl_const_ty_to_syn_ty, - webidl_const_v_to_backend_const_v, webidl_ty_to_syn_ty, TypePosition, -}; +use first_pass::{FirstPass, FirstPassRecord}; +use util::{public, webidl_const_ty_to_syn_ty, webidl_const_v_to_backend_const_v, TypePosition}; /// Either `Ok(t)` or `Err(failure::Error)`. pub type Result = ::std::result::Result; @@ -54,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) } @@ -93,48 +94,105 @@ fn compile_ast(mut ast: backend::ast::Program) -> String { } trait WebidlParse { - fn webidl_parse(&self, program: &mut backend::ast::Program, context: Ctx) -> Result<()>; + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'_>, + context: Ctx, + ) -> Result<()>; } -impl WebidlParse<()> for Vec { - fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> { +impl WebidlParse<()> for [webidl::ast::Definition] { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'_>, + (): (), + ) -> Result<()> { 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, ()) + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'_>, + (): (), + ) -> Result<()> { + match self { + webidl::ast::Definition::Enum(enumeration) => { + enumeration.webidl_parse(program, first_pass, ())? + } + webidl::ast::Definition::Includes(includes) => { + includes.webidl_parse(program, first_pass, ())? + } + webidl::ast::Definition::Interface(interface) => { + interface.webidl_parse(program, first_pass, ())? + } + webidl::ast::Definition::Typedef(typedef) => { + typedef.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, ()), // 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::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, _: ()) -> Result<()> { - match *self { - webidl::ast::Interface::NonPartial(ref interface) => { - interface.webidl_parse(program, ()) + 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(..) | webidl::ast::Interface::Partial(..) => { + webidl::ast::Interface::Callback(..) => { warn!("Unsupported WebIDL interface: {:?}", self); Ok(()) } @@ -143,13 +201,18 @@ impl WebidlParse<()> for webidl::ast::Interface { } impl WebidlParse<()> for webidl::ast::Typedef { - fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'_>, + (): (), + ) -> 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 +224,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, }); @@ -173,7 +234,12 @@ impl WebidlParse<()> for webidl::ast::Typedef { } impl WebidlParse<()> for webidl::ast::NonPartialInterface { - fn webidl_parse(&self, program: &mut backend::ast::Program, _: ()) -> Result<()> { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'_>, + (): (), + ) -> Result<()> { if util::is_chrome_only(&self.extended_attributes) { return Ok(()); } @@ -183,20 +249,44 @@ 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(), - }), - name: rust_ident(&self.name), + vis: public(), + name: rust_ident(self.name.to_camel_case().as_str()), attrs: Vec::new(), }), }); for extended_attribute in &self.extended_attributes { - extended_attribute.webidl_parse(program, self)?; + extended_attribute.webidl_parse(program, first_pass, self)?; } for member in &self.members { - member.webidl_parse(program, &self.name)?; + member.webidl_parse(program, first_pass, &self.name)?; + } + + Ok(()) + } +} + +impl WebidlParse<()> for webidl::ast::PartialInterface { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'_>, + (): (), + ) -> 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, first_pass, &self.name)?; } Ok(()) @@ -207,10 +297,11 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte fn webidl_parse( &self, program: &mut backend::ast::Program, + 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)); + 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,23 +325,19 @@ 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 { @@ -259,12 +346,12 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte ) 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 { @@ -275,7 +362,7 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte ) if lhs_name == "NamedConstructor" => { - add_constructor(rhs_arguments, rhs_name); + add_constructor(rhs_arguments, rhs_name) } webidl::ast::ExtendedAttribute::ArgumentList(_) | webidl::ast::ExtendedAttribute::Identifier(_) @@ -291,13 +378,22 @@ 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) + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'_>, + self_name: &'a str, + ) -> Result<()> { + match self { + 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) + } + webidl::ast::InterfaceMember::Const(cnst) => { + cnst.webidl_parse(program, first_pass, self_name) } - webidl::ast::InterfaceMember::Operation(ref op) => op.webidl_parse(program, self_name), - webidl::ast::InterfaceMember::Const(ref c) => c.webidl_parse(program, self_name), // TODO webidl::ast::InterfaceMember::Iterable(_) | webidl::ast::InterfaceMember::Maplike(_) @@ -309,11 +405,42 @@ 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> WebidlParse<&'a str> for webidl::ast::MixinMember { + 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, self_name), - webidl::ast::Attribute::Static(attr) => attr.webidl_parse(program, self_name), + 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); + 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); @@ -324,10 +451,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<()> { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'_>, + self_name: &'a str, + ) -> 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, 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); @@ -338,7 +470,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<()> { + 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(()); } @@ -346,25 +483,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)); } @@ -373,7 +514,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<()> { + 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(()); } @@ -381,25 +527,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)); } @@ -408,21 +558,28 @@ 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<()> { + 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); - 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(()) @@ -430,21 +587,28 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation { } impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation { - fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> { + 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); - 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(()) @@ -452,15 +616,18 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation { } 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, 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 @@ -479,23 +646,19 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::Const { fn webidl_parse( &self, program: &mut backend::ast::Program, - interface_name: &'a str, + _: &FirstPassRecord<'_>, + 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), - 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(()) } } diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 042fba08..93c7d050 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -2,12 +2,14 @@ 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; use webidl::ast::ExtendedAttribute; +use first_pass::FirstPassRecord; + fn shared_ref(ty: syn::Type) -> syn::Type { syn::TypeReference { and_token: Default::default(), @@ -17,79 +19,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; - } - }) -} - pub fn webidl_const_ty_to_syn_ty(ty: &webidl::ast::ConstType) -> syn::Type { use webidl::ast::ConstType::*; @@ -132,49 +61,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(), @@ -202,192 +88,330 @@ 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"); +impl<'a> FirstPassRecord<'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/consts.rs b/crates/webidl/tests/all/consts.rs index dd2265e0..716fbe8a 100644 --- a/crates/webidl/tests/all/consts.rs +++ b/crates/webidl/tests/all/consts.rs @@ -170,19 +170,19 @@ fn floats() { #[wasm_bindgen] pub fn test() { - assert_eq!(foo::floats::F, 0.0); - assert!(foo::floats::NEG_INF.is_infinite()); - assert!(foo::floats::NEG_INF.is_sign_negative()); - assert!(foo::floats::INF.is_infinite()); - assert!(foo::floats::INF.is_sign_positive()); - assert!(foo::floats::NAN.is_nan()); + assert_eq!(foo::Floats::F, 0.0_f32); + assert!(foo::Floats::NEG_INF.is_infinite()); + assert!(foo::Floats::NEG_INF.is_sign_negative()); + assert!(foo::Floats::INF.is_infinite()); + assert!(foo::Floats::INF.is_sign_positive()); + assert!(foo::Floats::NAN.is_nan()); - assert_eq!(foo::doubles::D, 0.0); - assert!(foo::doubles::NEG_INF.is_infinite()); - assert!(foo::doubles::NEG_INF.is_sign_negative()); - assert!(foo::doubles::INF.is_infinite()); - assert!(foo::doubles::INF.is_sign_positive()); - assert!(foo::doubles::NAN.is_nan()); + assert_eq!(foo::Doubles::D, 0.0_f64); + assert!(foo::Doubles::NEG_INF.is_infinite()); + assert!(foo::Doubles::NEG_INF.is_sign_negative()); + assert!(foo::Doubles::INF.is_infinite()); + assert!(foo::Doubles::INF.is_sign_positive()); + assert!(foo::Doubles::NAN.is_nan()); } "#, ) diff --git a/crates/webidl/tests/all/simple.rs b/crates/webidl/tests/all/simple.rs index ad177a08..3af57185 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(Foo::new(3.14159).unwrap())); - let tmp = pi.my_cmp(Foo::new(3.14159).unwrap()); - assert!(tmp); - let tmp =!pi.my_cmp(Foo::new(2.71828).unwrap()); - assert!(tmp); - let tmp = !e.my_cmp(Foo::new(3.14159).unwrap()); - assert!(tmp); - let tmp = e.my_cmp(Foo::new(2.71828).unwrap()); - assert!(tmp); + assert!(pi.my_cmp(&pi)); + assert!(!pi.my_cmp(&e)); + assert!(!e.my_cmp(&pi)); + assert!(e.my_cmp(&e)); } "#, ) @@ -370,3 +364,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(); +}