diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 0a495de5d..a95fbe619 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::BTreeMap; use proc_macro2::{Ident, Span}; use shared; use syn; @@ -21,7 +21,7 @@ pub struct Program { /// rust consts pub consts: Vec, /// rust submodules - pub modules: HashMap, + pub modules: BTreeMap, } /// A rust to js interface. Allows interaction with rust objects/functions @@ -227,8 +227,8 @@ pub enum ConstValue { /// /// This exists to give the ability to namespace js imports. #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] -#[derive(Default)] pub struct Module { + pub vis: syn::Visibility, /// js -> rust interfaces pub imports: Vec, } diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 1908441de..ac8f1096e 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1131,6 +1131,7 @@ impl<'a> TryToTokens for ModuleInIter<'a> { for i in imports.iter() { DescribeImport(&i.kind).to_tokens(tokens); } + let vis = &self.module.vis; let mut body = TokenStream::new(); for i in imports.iter() { if let Err(e) = i.kind.try_to_tokens(&mut body) { @@ -1139,7 +1140,7 @@ impl<'a> TryToTokens for ModuleInIter<'a> { } Diagnostic::from_vec(errors)?; (quote!{ - pub mod #name { + #vis mod #name { #body } }).to_tokens(tokens); diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 004dc31a4..09ffafbc8 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -41,17 +41,6 @@ pub(crate) struct InterfaceData<'src> { pub(crate) superclass: Option<&'src str>, } -impl<'src> Default for InterfaceData<'src> { - fn default() -> Self { - InterfaceData { - partial: true, - global: false, - operations: Default::default(), - superclass: Default::default(), - } - } -} - /// We need to collect namespace data during the first pass, to be used later. #[derive(Default)] pub(crate) struct NamespaceData<'src> { @@ -60,9 +49,10 @@ pub(crate) struct NamespaceData<'src> { pub(crate) operations: BTreeMap, OperationData<'src>>, } -impl<'src> Default for NamespaceData<'src> { - fn default() -> Self { - NamespaceData { +impl<'src> NamespaceData<'src> { + /// Same as `Default::default` but sets `partial` to true. + pub(crate) fn default_for_partial() -> Self { + Self { partial: true, operations: Default::default(), } @@ -178,7 +168,7 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { let interface = record .interfaces .entry(self.identifier.0) - .or_insert_with(Default::default); + .or_default(); interface.partial = false; interface.superclass = self.inheritance.map(|s| s.identifier.0); } @@ -348,6 +338,93 @@ impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> { } } +impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + { + let namespace = record + .namespaces + .entry(self.identifier.0) + .or_default(); + namespace.partial = false; + } + + if util::is_chrome_only(&self.attributes) { + return Ok(()) + } + + // We ignore all attributes. + + for member in &self.members.body { + member.first_pass(record, self.identifier.0)?; + } + + Ok(()) + } +} + +impl<'src> FirstPass<'src, ()> for weedle::PartialNamespaceDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + record + .namespaces + .entry(self.identifier.0) + .or_insert_with(NamespaceData::default_for_partial); + + if util::is_chrome_only(&self.attributes) { + return Ok(()) + } + + for member in &self.members.body { + member.first_pass(record, self.identifier.0)?; + } + + Ok(()) + } +} + +impl<'src> FirstPass<'src, &'src str> for weedle::namespace::NamespaceMember<'src> { + fn first_pass(&'src self, + record: &mut FirstPassRecord<'src>, + namespace_name: &'src str) -> Result<()> + { + match self { + weedle::namespace::NamespaceMember::Operation(op) => { + op.first_pass(record, namespace_name) + } + _ => Ok(()), + } + } +} + +impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> { + fn first_pass(&'src self, + record: &mut FirstPassRecord<'src>, + namespace_name: &'src str) -> Result<()> + { + let identifier = self.identifier.map(|s| s.0); + let arguments = &self.args.body.list; + let mut names = Vec::with_capacity(arguments.len()); + for argument in arguments { + match argument { + Argument::Single(arg) => names.push(arg.identifier.0), + Argument::Variadic(_) => return Ok(()), + } + } + record + .namespaces + .get_mut(namespace_name) + .unwrap() // call this after creating the namespace + .operations + .entry(identifier) + .and_modify(|operation_data| operation_data.overloaded = true) + .or_insert_with(Default::default) + .argument_names_same + .entry(names) + .and_modify(|same_argument_names| *same_argument_names = true) + .or_insert(false); + Ok(()) + } +} + impl<'a> FirstPassRecord<'a> { pub fn all_superclasses<'me>(&'me self, interface: &str) -> impl Iterator + 'me diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 7fcf04608..20bc0d443 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -774,10 +774,10 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> fn webidl_parse( &'src self, program: &mut backend::ast::Program, - record: &FirstPassRecord<'src>, + first_pass: &FirstPassRecord<'src>, self_name: &'src str, ) -> Result<()> { - let ty = match self.const_type.to_syn_type(record, TypePosition::Return) { + let ty = match self.const_type.to_syn_type(first_pass, TypePosition::Return) { Some(s) => s, None => return Ok(()), }; @@ -798,7 +798,7 @@ impl<'src> WebidlParse<'src, ()> for weedle::NamespaceDefinition<'src> { fn webidl_parse( &'src self, program: &mut backend::ast::Program, - record: &FirstPassRecord<'src>, + first_pass: &FirstPassRecord<'src>, (): (), ) -> Result<()> { if util::is_chrome_only(&self.attributes) { @@ -806,29 +806,59 @@ impl<'src> WebidlParse<'src, ()> for weedle::NamespaceDefinition<'src> { } let rust_name = rust_ident(self.identifier.0.to_snake_case().as_str()); - let js_name = &self.identifier.0; program.modules.entry(rust_name.clone()) .and_modify(|_| warn!("Namespace with rust name {:?} added more than once", rust_name)) - .or_default(); + .or_insert_with(|| backend::ast::Module { + vis: public(), + imports: Default::default() + }); - for member in self.members.body.iter() { - member.webidl_parse(program, record, js_name)? + if let Some(attrs) = &self.attributes { + for attr in &attrs.body.list { + attr.webidl_parse(program, first_pass, self)?; + } } + + let namespace_names = NamespaceNames { + rust_name: &rust_name, + js_name: &self.identifier.0, + }; + for member in &self.members.body { + member.webidl_parse(program, first_pass, namespace_names)?; + } + Ok(()) } } -impl<'src> WebidlParse<'src, &'src str> for weedle::namespace::NamespaceMember<'src> { +impl<'src> WebidlParse<'src, &'src weedle::NamespaceDefinition<'src>> for ExtendedAttribute<'src> { + fn webidl_parse( + &'src self, + _program: &mut backend::ast::Program, + _first_pass: &FirstPassRecord<'src>, + _namespace: &'src weedle::NamespaceDefinition<'src>, + ) -> Result<()> { + warn!("Unsupported WebIDL extended attribute: {:?}", self); + Ok(()) + } +} +#[derive(Copy, Clone)] +struct NamespaceNames<'a> { + rust_name: &'a Ident, + js_name: &'a str, +} + +impl<'src> WebidlParse<'src, NamespaceNames<'src>> for weedle::namespace::NamespaceMember<'src> { fn webidl_parse( &'src self, program: &mut backend::ast::Program, - record: &FirstPassRecord<'src>, - module_name: &'src str + first_pass: &FirstPassRecord<'src>, + ns_names: NamespaceNames<'src> ) -> Result<()> { match self { weedle::namespace::NamespaceMember::Operation(op) => { - op.webidl_parse(program, record, module_name)?; + op.webidl_parse(program, first_pass, ns_names)?; } weedle::namespace::NamespaceMember::Attribute(_) => { warn!("Attribute namespace members are not supported") @@ -838,117 +868,42 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::namespace::NamespaceMember<' } } -impl<'src> WebidlParse<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> { +impl<'src> WebidlParse<'src, NamespaceNames<'src>> for weedle::namespace::OperationNamespaceMember<'src> { fn webidl_parse( &'src self, program: &mut backend::ast::Program, - record: &FirstPassRecord<'src>, - module_name: &'src str + first_pass: &FirstPassRecord<'src>, + ns_names: NamespaceNames<'src> ) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } - let structural = util::is_structural(&self.attributes); + let imported_fn = match first_pass.create_namespace_operation( + &self.args.body.list, + self.identifier.as_ref().map(|id| id.0), + &self.return_type, + ns_names.js_name, + util::throws(&self.attributes) + ) { + Some(f) => f, + None => { return Ok(()) } + }; - let name = match &self.identifier { - Some(ident) => ident.0, - None => { - warn!("Operations without identifiers are not supported"); - return Ok(()); - } + let import = backend::ast::Import { + module: None, + js_namespace: Some(ns_names.js_name.clone()), + kind: backend::ast::ImportKind::Function(imported_fn), + }; + + program + .modules + .get_mut(ns_names.rust_name) + .unwrap() + .imports + .push(import); - let import_fn = first_pass - .create_function( - name, - false, - same_argument_names: bool, - arguments: &[Argument], - mut ret: Option, - kind: backend::ast::ImportFunctionKind, - structural: bool, - catch: bool, - doc_comment: Option, - ) -> Option - .create_basic_method( - args, - match identifier.map(|s| s.0) { - Some(ref name) if specials.is_empty() => - ::first_pass::OperationId::Operation(Some(name.clone())), - }, - &self.return_type, - self_name, - specials.len() == 1 || first_pass - .interfaces - .get(self_name) - .map(|interface_data| interface_data.global) - .unwrap_or(false), - util::throws(&self.attributes), - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); Ok(()) } } -fn member_operation<'src>( - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - self_name: &'src str, - attrs: &'src Option, - modifier: Option, - specials: &[weedle::interface::Special], - return_type: &'src weedle::types::ReturnType<'src>, - args: &'src [Argument], - identifier: &Option>, -) -> Result<()> { - use weedle::interface::StringifierOrStatic::*; - - if util::is_chrome_only(attrs) { - return Ok(()); - } - let statik = match modifier { - Some(Stringifier(_)) => { - warn!("Unsupported stringifier on type {:?}", (self_name, identifier)); - return Ok(()) - } - Some(Static(_)) => true, - None => false, - }; - - first_pass - .create_basic_method( - args, - match identifier.map(|s| s.0) { - None if specials.is_empty() => ::first_pass::OperationId::Operation(None), - None if specials.len() == 1 => match specials[0] { - weedle::interface::Special::Getter(weedle::term::Getter) => - ::first_pass::OperationId::IndexingGetter, - weedle::interface::Special::Setter(weedle::term::Setter) => - ::first_pass::OperationId::IndexingSetter, - weedle::interface::Special::Deleter(weedle::term::Deleter) => - ::first_pass::OperationId::IndexingDeleter, - weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller) => - return Ok(()), - }, - Some(ref name) if specials.is_empty() => - ::first_pass::OperationId::Operation(Some(name.clone())), - _ => { - warn!("Unsupported specials on type {:?}", (self_name, identifier)); - return Ok(()) - } - }, - return_type, - self_name, - statik, - specials.len() == 1 || first_pass - .interfaces - .get(self_name) - .map(|interface_data| interface_data.global) - .unwrap_or(false), - util::throws(attrs), - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - Ok(()) -} diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 3320c38b1..f654937b6 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1029,33 +1029,25 @@ impl<'src> FirstPassRecord<'src> { pub fn create_namespace_operation( &self, arguments: &[weedle::argument::Argument], - operation_name: &str, + operation_name: Option<&str>, return_type: &weedle::types::ReturnType, - structural: bool, + namespace_name: &str, catch: bool, ) -> Option { let (overloaded, same_argument_names) = self.get_namespaced_operation_overloading( arguments, - &first_pass::OperationId::Operation(Some(operation_name)) - self_name, + operation_name, + namespace_name, ); - let name = match &operation_id { - first_pass::OperationId::Operation(name) => match name { - None => { - warn!("Operations without a name are unsupported"); - return None; - } - Some(name) => name.to_string(), - }, - _ => { - warn!("operations in namespaces must be normal functions"); + let name = match operation_name { + Some(name) => name.to_string(), + None => { + warn!("Operations without a name are unsupported"); return None; } }; - let kind = backend::ast::ImportFunctionKind::Normal; - let ret = match return_type { weedle::types::ReturnType::Void(_) => None, weedle::types::ReturnType::Type(ty) => { @@ -1069,8 +1061,9 @@ impl<'src> FirstPassRecord<'src> { } }; let doc_comment = format!("The `{}.{}()` function\n\n{}", + namespace_name, name, - mdn_doc(self_name, Some(&name))); + mdn_doc(namespace_name, Some(&name))); // TODO check link self.create_function( &name, @@ -1078,10 +1071,10 @@ impl<'src> FirstPassRecord<'src> { same_argument_names, arguments, ret, - kind, - structural, + backend::ast::ImportFunctionKind::Normal, + false, catch, - doc_comment, + Some(doc_comment), ) } @@ -1090,14 +1083,14 @@ impl<'src> FirstPassRecord<'src> { pub fn get_namespaced_operation_overloading( &self, arguments: &[weedle::argument::Argument], - id: &first_pass::OperationId, + operation_name: Option<&str>, namespace_name: &str, ) -> (bool, bool) { let data = match self.namespaces.get(namespace_name) { Some(data) => data, None => return (false, false), }; - let data = match data.operations.get(id) { + let data = match data.operations.get(&operation_name) { Some(data) => data, None => return (false, false), };