From 03eb1b1d01d7eafcb27ef476fa3fa45743ebe835 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 7 Aug 2018 15:50:27 -0700 Subject: [PATCH] Support named "special" operations in WebIDL This commit adds support for two different features of the "special" operations in WebIDL. First, it implements the desugaring [described by WebIDL][1] where this: interface Dictionary { getter double getProperty(DOMString propertyName); setter void setProperty(DOMString propertyName, double propertyValue); }; becomes ... interface Dictionary { double getProperty(DOMString propertyName); void setProperty(DOMString propertyName, double propertyValue); getter double (DOMString propertyName); setter void (DOMString propertyName, double propertyValue); }; where specifically a named `getter` generates both a getter and a named function. Second it implements the distinction between two different types of getters in WebIDL, described as: > Getters and setters come in two varieties: ones that take a DOMString as a > property name, known as named property getters and named property setters, and > ones that take an unsigned long as a property index, known as indexed property > getters and indexed property setters. The name `get` is given to DOMString arguments, and the name `get_idx` is given to index property getters. [1]: https://heycam.github.io/webidl/#idl-special-operations --- crates/web-sys/build.rs | 1 + crates/webidl/src/first_pass.rs | 57 +++++++++++++------------ crates/webidl/src/lib.rs | 76 +++++++++++++++++++++------------ crates/webidl/src/util.rs | 12 +++--- 4 files changed, 85 insertions(+), 61 deletions(-) diff --git a/crates/web-sys/build.rs b/crates/web-sys/build.rs index 630975c96..5eef5158c 100644 --- a/crates/web-sys/build.rs +++ b/crates/web-sys/build.rs @@ -65,6 +65,7 @@ fn try_main() -> Result<(), failure::Error> { .context("writing bindings to output file")?; // run rustfmt on the generated file - really handy for debugging + println!("cargo:rerun-if-env-changed=WEBIDL_RUSTFMT_BINDINGS"); if env::var("WEBIDL_RUSTFMT_BINDINGS").is_ok() { let status = Command::new("rustfmt") .arg(&out_file_path) diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index f3637f610..5011d6f66 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -11,7 +11,7 @@ use std::collections::{BTreeMap, BTreeSet}; use weedle::argument::Argument; use weedle::attribute::ExtendedAttribute; -use weedle::interface::StringifierOrStatic; +use weedle::interface::{StringifierOrStatic, Special}; use weedle::mixin::MixinMember; use weedle::namespace::NamespaceMember; use weedle; @@ -61,7 +61,7 @@ pub(crate) struct NamespaceData<'src> { pub(crate) operations: BTreeMap, OperationData<'src>>, } -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] pub(crate) enum OperationId<'src> { Constructor, Operation(Option<&'src str>), @@ -171,7 +171,7 @@ fn first_pass_operation<'src>( record: &mut FirstPassRecord<'src>, first_pass_operation_type: FirstPassOperationType, self_name: &'src str, - id: OperationId<'src>, + ids: &[OperationId<'src>], arguments: &[Argument<'src>], ) -> Result<()> { let mut names = Vec::with_capacity(arguments.len()); @@ -181,7 +181,7 @@ fn first_pass_operation<'src>( Argument::Variadic(variadic) => names.push(variadic.identifier.0), } } - match first_pass_operation_type{ + let operations = match first_pass_operation_type{ FirstPassOperationType::Interface => { &mut record .interfaces @@ -203,14 +203,17 @@ fn first_pass_operation<'src>( .expect(&format!("not found {} namesace", self_name)) .operations }, + }; + for id in ids { + operations + .entry(*id) + .and_modify(|operation_data| operation_data.overloaded = true) + .or_default() + .argument_names_same + .entry(names.clone()) + .and_modify(|same_argument_names| *same_argument_names = true) + .or_insert(false); } - .entry(id) - .and_modify(|operation_data| operation_data.overloaded = true) - .or_default() - .argument_names_same - .entry(names) - .and_modify(|same_argument_names| *same_argument_names = true) - .or_insert(false); Ok(()) } @@ -278,7 +281,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { record, FirstPassOperationType::Interface, self_name, - OperationId::Constructor, + &[OperationId::Constructor], &list.args.body.list, ) } @@ -287,7 +290,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { record, FirstPassOperationType::Interface, self_name, - OperationId::Constructor, + &[OperationId::Constructor], &[], ) } @@ -298,7 +301,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { record, FirstPassOperationType::Interface, self_name, - OperationId::Constructor, + &[OperationId::Constructor], &list.args.body.list, ) } @@ -332,7 +335,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM return Ok(()); } - if !self.specials.is_empty() && self.specials.len() != 1 { + if self.specials.len() > 1 { warn!("Unsupported webidl operation {:?}", self); return Ok(()) } @@ -340,20 +343,20 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM warn!("Unsupported webidl operation {:?}", self); return Ok(()) } + let mut ids = vec![OperationId::Operation(self.identifier.map(|s| s.0))]; + for special in self.specials.iter() { + ids.push(match special { + Special::Getter(_) => OperationId::IndexingGetter, + Special::Setter(_) => OperationId::IndexingSetter, + Special::Deleter(_) => OperationId::IndexingDeleter, + Special::LegacyCaller(_) => continue, + }); + } first_pass_operation( record, FirstPassOperationType::Interface, self_name, - match self.identifier.map(|s| s.0) { - None => match self.specials.get(0) { - None => OperationId::Operation(None), - Some(weedle::interface::Special::Getter(_)) => OperationId::IndexingGetter, - Some(weedle::interface::Special::Setter(_)) => OperationId::IndexingSetter, - Some(weedle::interface::Special::Deleter(_)) => OperationId::IndexingDeleter, - Some(weedle::interface::Special::LegacyCaller(_)) => return Ok(()), - }, - Some(ref name) => OperationId::Operation(Some(name.clone())), - }, + &ids, &self.args.body.list, ) } @@ -434,7 +437,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'s record, FirstPassOperationType::Mixin, self_name, - OperationId::Operation(self.identifier.map(|s| s.0.clone())), + &[OperationId::Operation(self.identifier.map(|s| s.0.clone()))], &self.args.body.list, ) } @@ -524,7 +527,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceM record, FirstPassOperationType::Namespace, self_name, - OperationId::Operation(self.identifier.map(|s| s.0.clone())), + &[OperationId::Operation(self.identifier.map(|s| s.0.clone()))], &self.args.body.list, ) } diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 23163f9fc..c0c419c71 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -44,7 +44,7 @@ use proc_macro2::{Ident, Span}; use weedle::argument::Argument; use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList}; -use first_pass::{FirstPass, FirstPassRecord}; +use first_pass::{FirstPass, FirstPassRecord, OperationId}; use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc}; use idl_type::{IdlType, ToIdlType}; @@ -646,6 +646,7 @@ fn member_operation<'src>( identifier: &Option>, ) -> Result<()> { use weedle::interface::StringifierOrStatic::*; + use weedle::interface::Special; if util::is_chrome_only(attrs) { return Ok(()); @@ -660,33 +661,52 @@ fn member_operation<'src>( None => false, }; - for import_function in 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(_) => ::first_pass::OperationId::IndexingGetter, - weedle::interface::Special::Setter(_) => ::first_pass::OperationId::IndexingSetter, - weedle::interface::Special::Deleter(_) => ::first_pass::OperationId::IndexingDeleter, - weedle::interface::Special::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, - is_static, - specials.len() == 1 || first_pass - .interfaces - .get(self_name) - .map(|interface_data| interface_data.global) - .unwrap_or(false), - util::throws(attrs), - ) { - program.imports.push(wrap_import_function(import_function)); + let mut operation_ids = vec![ + OperationId::Operation(identifier.map(|s| s.0)), + ]; + if specials.len() > 1 { + warn!( + "Unsupported specials ({:?}) on type {:?}", + specials, + (self_name, identifier), + ); + return Ok(()) + } else if specials.len() == 1 { + let id = match specials[0] { + Special::Getter(weedle::term::Getter) => OperationId::IndexingGetter, + Special::Setter(weedle::term::Setter) => OperationId::IndexingSetter, + Special::Deleter(weedle::term::Deleter) => OperationId::IndexingDeleter, + Special::LegacyCaller(weedle::term::LegacyCaller) => return Ok(()), + }; + operation_ids.push(id); + } + + for id in operation_ids { + let methods = first_pass + .create_basic_method( + args, + id, + return_type, + self_name, + is_static, + match id { + OperationId::IndexingGetter | + OperationId::IndexingSetter | + OperationId::IndexingDeleter => true, + _ => { + first_pass + .interfaces + .get(self_name) + .map(|interface_data| interface_data.global) + .unwrap_or(false) + } + }, + util::throws(attrs), + ); + + for method in methods { + program.imports.push(wrap_import_function(method)); + } } Ok(()) } diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 0722d2c87..80e31c9cd 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -370,7 +370,7 @@ impl<'src> FirstPassRecord<'src> { /// Create a wasm-bindgen method, if possible. pub fn create_basic_method( &self, - arguments: &[weedle::argument::Argument], + arguments: &[Argument], operation_id: first_pass::OperationId, return_type: &weedle::types::ReturnType, self_name: &str, @@ -392,11 +392,11 @@ impl<'src> FirstPassRecord<'src> { warn!("Operations without a name are unsupported"); return Vec::new(); } - Some(name) => name.to_string(), + Some(name) => name, }, - first_pass::OperationId::IndexingGetter => "get".to_string(), - first_pass::OperationId::IndexingSetter => "set".to_string(), - first_pass::OperationId::IndexingDeleter => "delete".to_string(), + first_pass::OperationId::IndexingGetter => "get", + first_pass::OperationId::IndexingSetter => "set", + first_pass::OperationId::IndexingDeleter => "delete", }; let kind = backend::ast::ImportFunctionKind::Method { @@ -455,7 +455,7 @@ impl<'src> FirstPassRecord<'src> { /// whether there overloads with same argument names for given argument types pub fn get_operation_overloading( &self, - arguments: &[weedle::argument::Argument], + arguments: &[Argument], operation_id: &first_pass::OperationId, self_name: &str, namespace: bool,