diff --git a/crates/webidl-tests/simple.js b/crates/webidl-tests/simple.js index bb1ace6b..d6859032 100644 --- a/crates/webidl-tests/simple.js +++ b/crates/webidl-tests/simple.js @@ -142,3 +142,7 @@ global.MixinFoo = class MixinFoo { this._bar += other; } }; + +global.Overloads = class { + foo() {} +}; diff --git a/crates/webidl-tests/simple.rs b/crates/webidl-tests/simple.rs index b59a9ad9..c9911a1d 100644 --- a/crates/webidl-tests/simple.rs +++ b/crates/webidl-tests/simple.rs @@ -114,3 +114,11 @@ fn mixin() { f.add_to_bar(MixinFoo::default_bar()); assert_eq!(f.bar(), 8); } + +#[wasm_bindgen_test] +fn overload_naming() { + let o = Overloads::new().unwrap(); + o.foo(); + o.foo_with_arg("x"); + o.foo_with_arg_and_a("x", 3); +} diff --git a/crates/webidl-tests/simple.webidl b/crates/webidl-tests/simple.webidl index 6df6de63..63d7b9d2 100644 --- a/crates/webidl-tests/simple.webidl +++ b/crates/webidl-tests/simple.webidl @@ -88,3 +88,10 @@ partial interface mixin MixinBar { }; MixinFoo includes MixinBar; + +[Constructor()] +interface Overloads { + void foo(); + void foo(DOMString arg, optional long a); + void foo(DOMString arg, (float or short) b); +}; diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index ad93f575..a0614768 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -11,10 +11,10 @@ use std::collections::{BTreeMap, BTreeSet}; use weedle::{DictionaryDefinition, PartialDictionaryDefinition}; use weedle::argument::Argument; -use weedle::attribute::ExtendedAttribute; -use weedle::interface::{StringifierOrStatic, Special}; -use weedle::mixin::MixinMember; -use weedle::namespace::NamespaceMember; +use weedle::attribute::*; +use weedle::interface::*; +use weedle::mixin::*; +use weedle::namespace::OperationNamespaceMember; use weedle; use super::Result; @@ -25,7 +25,7 @@ use util::camel_case_ident; #[derive(Default)] pub(crate) struct FirstPassRecord<'src> { pub(crate) interfaces: BTreeMap<&'src str, InterfaceData<'src>>, - pub(crate) enums: BTreeSet<&'src str>, + pub(crate) enums: BTreeMap<&'src str, &'src weedle::EnumDefinition<'src>>, /// The mixins, mapping their name to the webidl ast node for the mixin. pub(crate) mixins: BTreeMap<&'src str, MixinData<'src>>, pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>, @@ -40,8 +40,13 @@ pub(crate) struct InterfaceData<'src> { /// Whether only partial interfaces were encountered pub(crate) partial: bool, pub(crate) global: bool, + pub(crate) attributes: Vec<&'src AttributeInterfaceMember<'src>>, + pub(crate) consts: Vec<&'src ConstMember<'src>>, + pub(crate) methods: Vec<&'src OperationInterfaceMember<'src>>, pub(crate) operations: BTreeMap, OperationData<'src>>, pub(crate) superclass: Option<&'src str>, + pub(crate) definition_attributes: Option<&'src [ExtendedAttribute<'src>]>, + pub(crate) constructors: Vec<(&'src str, &'src [Argument<'src>])>, } /// We need to collect mixin data during the first pass, to be used later. @@ -49,7 +54,9 @@ pub(crate) struct InterfaceData<'src> { pub(crate) struct MixinData<'src> { /// Whether only partial mixins were encountered pub(crate) partial: bool, - pub(crate) members: Vec<&'src MixinMember<'src>>, + pub(crate) attributes: Vec<&'src AttributeMixinMember<'src>>, + pub(crate) consts: Vec<&'src ConstMember<'src>>, + pub(crate) methods: Vec<&'src OperationMixinMember<'src>>, pub(crate) operations: BTreeMap, OperationData<'src>>, } @@ -57,8 +64,7 @@ pub(crate) struct MixinData<'src> { #[derive(Default)] pub(crate) struct NamespaceData<'src> { /// Whether only partial namespaces were encountered - pub(crate) partial: bool, - pub(crate) members: Vec<&'src NamespaceMember<'src>>, + pub(crate) members: Vec<&'src OperationNamespaceMember<'src>>, pub(crate) operations: BTreeMap, OperationData<'src>>, } @@ -117,8 +123,14 @@ impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> { Namespace(namespace) => namespace.first_pass(record, ()), PartialNamespace(namespace) => namespace.first_pass(record, ()), Typedef(typedef) => typedef.first_pass(record, ()), - _ => { - // Other definitions aren't currently used in the first pass + + Implements(_) => Ok(()), + Callback(..) => { + warn!("Unsupported WebIDL Callback definition: {:?}", self); + Ok(()) + } + CallbackInterface(..) => { + warn!("Unsupported WebIDL CallbackInterface definition: {:?}", self); Ok(()) } } @@ -127,6 +139,10 @@ impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> { impl<'src> FirstPass<'src, ()> for weedle::DictionaryDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + record.dictionaries.entry(self.identifier.0) .or_default() .definition = Some(self); @@ -136,6 +152,10 @@ impl<'src> FirstPass<'src, ()> for weedle::DictionaryDefinition<'src> { impl<'src> FirstPass<'src, ()> for weedle::PartialDictionaryDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + record.dictionaries.entry(self.identifier.0) .or_default() .partials @@ -150,7 +170,7 @@ impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> { return Ok(()); } - if !record.enums.insert(self.identifier.0) { + if record.enums.insert(self.identifier.0, self).is_some() { info!("Encountered multiple enum declarations: {}", self.identifier.0); } @@ -187,7 +207,7 @@ fn first_pass_operation<'src>( self_name: &'src str, ids: &[OperationId<'src>], arguments: &[Argument<'src>], -) -> Result<()> { +) { let mut names = Vec::with_capacity(arguments.len()); for argument in arguments { match argument { @@ -228,8 +248,6 @@ fn first_pass_operation<'src>( .and_modify(|same_argument_names| *same_argument_names = true) .or_insert(false); } - - Ok(()) } impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { @@ -238,6 +256,11 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { return Ok(()); } + if util::is_no_interface_object(&self.attributes) { + info!("Skipping because of `NoInterfaceObject` attribute: {:?}", self.identifier.0); + return Ok(()); + } + { let interface_data = record .interfaces @@ -245,11 +268,16 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { .or_default(); interface_data.partial = false; interface_data.superclass = self.inheritance.map(|s| s.identifier.0); + interface_data.definition_attributes = self.attributes.as_ref() + .map(|l| &l.body.list[..]); } - if let Some(attrs) = &self.attributes { - for attr in &attrs.body.list { - attr.first_pass(record, self.identifier.0)?; + for attr in attrs.body.list.iter() { + process_interface_attribute( + record, + self.identifier.0, + attr, + ); } } @@ -261,84 +289,123 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { } } +fn process_interface_attribute<'src>( + record: &mut FirstPassRecord<'src>, + self_name: &'src str, + attr: &'src ExtendedAttribute<'src> +) { + match attr { + ExtendedAttribute::ArgList(list) + if list.identifier.0 == "Constructor" => + { + record.interfaces + .get_mut(self_name) + .unwrap() + .constructors.push((self_name, &list.args.body.list)); + first_pass_operation( + record, + FirstPassOperationType::Interface, + self_name, + &[OperationId::Constructor], + &list.args.body.list, + ) + } + ExtendedAttribute::NoArgs(other) if (other.0).0 == "Constructor" => { + record.interfaces + .get_mut(self_name) + .unwrap() + .constructors.push((self_name, &[])); + first_pass_operation( + record, + FirstPassOperationType::Interface, + self_name, + &[OperationId::Constructor], + &[], + ) + } + ExtendedAttribute::NamedArgList(list) + if list.lhs_identifier.0 == "NamedConstructor" => + { + record.interfaces + .get_mut(self_name) + .unwrap() + .constructors.push((list.rhs_identifier.0, &list.args.body.list)); + first_pass_operation( + record, + FirstPassOperationType::Interface, + self_name, + &[OperationId::Constructor], + &list.args.body.list, + ) + } + ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Global" => { + record.interfaces.get_mut(self_name).unwrap().global = true; + } + ExtendedAttribute::IdentList(id) if id.identifier.0 == "Global" => { + record.interfaces.get_mut(self_name).unwrap().global = true; + } + _ => {} + } +} + impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } - record .interfaces .entry(self.identifier.0) .or_insert_with(|| InterfaceData { partial: true, - global: false, - operations: Default::default(), - superclass: None, + ..Default::default() }, ); - for member in &self.members.body { member.first_pass(record, self.identifier.0)?; } - Ok(()) } } -impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { - fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { - match self { - ExtendedAttribute::ArgList(list) if list.identifier.0 == "Constructor" => { - first_pass_operation( - record, - FirstPassOperationType::Interface, - self_name, - &[OperationId::Constructor], - &list.args.body.list, - ) - } - ExtendedAttribute::NoArgs(name) if (name.0).0 == "Constructor" => { - first_pass_operation( - record, - FirstPassOperationType::Interface, - self_name, - &[OperationId::Constructor], - &[], - ) - } - ExtendedAttribute::NamedArgList(list) - if list.lhs_identifier.0 == "NamedConstructor" => - { - first_pass_operation( - record, - FirstPassOperationType::Interface, - self_name, - &[OperationId::Constructor], - &list.args.body.list, - ) - } - ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Global" => { - record.interfaces.get_mut(self_name).unwrap().global = true; - Ok(()) - } - ExtendedAttribute::IdentList(id) if id.identifier.0 == "Global" => { - record.interfaces.get_mut(self_name).unwrap().global = true; - Ok(()) - } - _ => Ok(()) - } - } -} - impl<'src> FirstPass<'src, &'src str> for weedle::interface::InterfaceMember<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { match self { - weedle::interface::InterfaceMember::Operation(op) => { + InterfaceMember::Attribute(attr) => { + attr.first_pass(record, self_name) + } + InterfaceMember::Operation(op) => { op.first_pass(record, self_name) } - _ => Ok(()), + InterfaceMember::Const(const_) => { + if util::is_chrome_only(&const_.attributes) { + return Ok(()); + } + record.interfaces + .get_mut(self_name) + .unwrap() + .consts + .push(const_); + Ok(()) + } + InterfaceMember::Iterable(_iterable) => { + warn!("Unsupported WebIDL iterable interface member: {:?}", self); + Ok(()) + } + // TODO + InterfaceMember::Maplike(_) => { + warn!("Unsupported WebIDL Maplike interface member: {:?}", self); + Ok(()) + } + InterfaceMember::Stringifier(_) => { + warn!("Unsupported WebIDL Stringifier interface member: {:?}", self); + Ok(()) + } + InterfaceMember::Setlike(_) => { + warn!("Unsupported WebIDL Setlike interface member: {:?}", self); + Ok(()) + } } } } @@ -357,6 +424,13 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM warn!("Unsupported webidl stringifier: {:?}", self); return Ok(()) } + + record.interfaces + .get_mut(self_name) + .unwrap() + .methods + .push(self); + let mut ids = vec![OperationId::Operation(self.identifier.map(|s| s.0))]; for special in self.specials.iter() { ids.push(match special { @@ -372,7 +446,23 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM self_name, &ids, &self.args.body.list, - ) + ); + Ok(()) + } +} + +impl<'src> FirstPass<'src, &'src str> for weedle::interface::AttributeInterfaceMember<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + + record.interfaces + .get_mut(self_name) + .unwrap() + .attributes + .push(self); + Ok(()) } } @@ -388,7 +478,6 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src>{ .entry(self.identifier.0) .or_default(); mixin_data.partial = false; - mixin_data.members.extend(&self.members.body); } for member in &self.members.body { @@ -411,12 +500,9 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src> .or_insert_with(|| MixinData { partial: true, - members: Default::default(), - operations: Default::default(), + ..Default::default() }, - ) - .members - .extend(&self.members.body); + ); for member in &self.members.body { member.first_pass(record, self.identifier.0)?; @@ -429,10 +515,23 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src> impl<'src> FirstPass<'src, &'src str> for weedle::mixin::MixinMember<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { match self { - weedle::mixin::MixinMember::Operation(op) => { - op.first_pass(record, self_name) + MixinMember::Operation(op) => op.first_pass(record, self_name), + MixinMember::Attribute(a) => a.first_pass(record, self_name), + MixinMember::Const(a) => { + if util::is_chrome_only(&a.attributes) { + return Ok(()); + } + record.mixins + .get_mut(self_name) + .unwrap() + .consts + .push(a); + Ok(()) + } + MixinMember::Stringifier(_) => { + warn!("Unsupported WebIDL stringifier mixin member: {:?}", self); + Ok(()) } - _ => Ok(()), } } } @@ -448,13 +547,34 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'s return Ok(()) } + record.mixins + .get_mut(self_name) + .unwrap() + .methods + .push(self); + first_pass_operation( record, FirstPassOperationType::Mixin, self_name, &[OperationId::Operation(self.identifier.map(|s| s.0.clone()))], &self.args.body.list, - ) + ); + Ok(()) + } +} + +impl<'src> FirstPass<'src, &'src str> for weedle::mixin::AttributeMixinMember<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + record.mixins + .get_mut(self_name) + .unwrap() + .attributes + .push(self); + Ok(()) } } @@ -481,16 +601,12 @@ impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> { record .namespaces .entry(self.identifier.0) - .and_modify(|namespace_data| namespace_data.partial = false) .or_insert_with(|| NamespaceData { - partial: true, members: Default::default(), operations: Default::default(), }, - ) - .members - .extend(&self.members.body); + ); for member in &self.members.body { member.first_pass(record, self.identifier.0)?; @@ -509,9 +625,7 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialNamespaceDefinition<'src> { record .namespaces .entry(self.identifier.0) - .or_default() - .members - .extend(&self.members.body); + .or_default(); for member in &self.members.body { member.first_pass(record, self.identifier.0)?; @@ -538,13 +652,16 @@ impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceM return Ok(()) } + record.namespaces.get_mut(self_name).unwrap().members.push(self); + first_pass_operation( record, FirstPassOperationType::Namespace, self_name, &[OperationId::Operation(self.identifier.map(|s| s.0.clone()))], &self.args.body.list, - ) + ); + Ok(()) } } diff --git a/crates/webidl/src/idl_type.rs b/crates/webidl/src/idl_type.rs index 1f88d008..3992b20d 100644 --- a/crates/webidl/src/idl_type.rs +++ b/crates/webidl/src/idl_type.rs @@ -287,7 +287,7 @@ impl<'a> ToIdlType<'a> for Identifier<'a> { Some(IdlType::Interface(self.0)) } else if record.dictionaries.contains_key(self.0) { Some(IdlType::Dictionary(self.0)) - } else if record.enums.contains(self.0) { + } else if record.enums.contains_key(self.0) { Some(IdlType::Enum(self.0)) } else { warn!("Unrecognized type: {}", self.0); diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index af09eaa9..d1b4205e 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -44,7 +44,7 @@ use failure::ResultExt; use heck::{ShoutySnakeCase, SnakeCase}; use proc_macro2::{Ident, Span}; use weedle::argument::Argument; -use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList}; +use weedle::attribute::{ExtendedAttributeList}; use weedle::dictionary::DictionaryMember; use first_pass::{FirstPass, FirstPassRecord, OperationId}; @@ -81,13 +81,6 @@ fn parse(webidl_source: &str) -> Result { format_err!("failed to parse WebIDL") .context(ErrorKind::ParsingWebIDLSourcePos(pos)).into() } - // webidl::ParseError::UnrecognizedToken { token: Some((start, ..)), .. } => { - // ErrorKind::ParsingWebIDLSourcePos(*start) - // } - // webidl::ParseError::ExtraToken { token: (start, ..) } => { - // ErrorKind::ParsingWebIDLSourcePos(*start) - // }, - // _ => ErrorKind::ParsingWebIDLSource }); } }; @@ -95,7 +88,19 @@ fn parse(webidl_source: &str) -> Result { 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, ())?; + + for e in first_pass_record.enums.values() { + first_pass_record.append_enum(&mut program, e); + } + for d in first_pass_record.dictionaries.values() { + first_pass_record.append_dictionary(&mut program, d); + } + for (name, n) in first_pass_record.namespaces.iter() { + first_pass_record.append_ns(&mut program, name, n); + } + for (name, d) in first_pass_record.interfaces.iter() { + first_pass_record.append_interface(&mut program, name, d); + } Ok(program) } @@ -145,673 +150,19 @@ fn compile_ast(mut ast: backend::ast::Program) -> String { tokens.to_string() } -/// The main trait for parsing WebIDL AST into wasm-bindgen AST. -trait WebidlParse<'src, Ctx> { - /// Parse `self` into wasm-bindgen AST, and insert it into `program`. - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - context: Ctx, - ) -> Result<()>; -} - -impl<'src> WebidlParse<'src, ()> for [weedle::Definition<'src>] { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - (): (), - ) -> Result<()> { - for def in self { - def.webidl_parse(program, first_pass, ())?; - } - Ok(()) - } -} - -impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - (): (), - ) -> Result<()> { - match self { - weedle::Definition::Enum(enumeration) => { - enumeration.webidl_parse(program, first_pass, ())? - } - weedle::Definition::Interface(interface) => { - interface.webidl_parse(program, first_pass, ())? - } - weedle::Definition::PartialInterface(interface) => { - interface.webidl_parse(program, first_pass, ())? - } - | weedle::Definition::Typedef(_) - | weedle::Definition::InterfaceMixin(_) - | weedle::Definition::PartialInterfaceMixin(_) - | weedle::Definition::IncludesStatement(..) - | weedle::Definition::PartialDictionary(..) - | weedle::Definition::PartialNamespace(..)=> { - // handled in the first pass - } - weedle::Definition::Implements(..) => { - // nothing to do for this, ignore it - } - weedle::Definition::Namespace(namespace) => { - namespace.webidl_parse(program, first_pass, ())? - } - weedle::Definition::Dictionary(dict) => { - dict.webidl_parse(program, first_pass, ())? - } - - // TODO - weedle::Definition::Callback(..) => { - warn!("Unsupported WebIDL Callback definition: {:?}", self) - } - weedle::Definition::CallbackInterface(..) => { - warn!("Unsupported WebIDL CallbackInterface definition: {:?}", self) - } - } - Ok(()) - } -} - -impl<'src> WebidlParse<'src, ()> for weedle::InterfaceDefinition<'src> { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - (): (), - ) -> Result<()> { - if util::is_chrome_only(&self.attributes) { - info!("Skipping because of `ChromeOnly` attribute: {:?}", self); - return Ok(()); - } - - if util::is_no_interface_object(&self.attributes) { - info!("Skipping because of `NoInterfaceObject` attribute: {:?}", self); - return Ok(()); - } - - let doc_comment = Some(format!( - "The `{}` object\n\n{}", - self.identifier.0, - mdn_doc(self.identifier.0, None), - )); - - program.imports.push(backend::ast::Import { - module: None, - js_namespace: None, - kind: backend::ast::ImportKind::Type(backend::ast::ImportType { - vis: public(), - rust_name: rust_ident(camel_case_ident(self.identifier.0).as_str()), - js_name: self.identifier.0.to_string(), - attrs: Vec::new(), - doc_comment, - instanceof_shim: format!("__widl_instanceof_{}", self.identifier.0), - extends: first_pass.all_superclasses(self.identifier.0) - .map(|name| Ident::new(&name, Span::call_site())) - .collect(), - }), - }); - - if let Some(attrs) = &self.attributes { - for attr in &attrs.body.list { - attr.webidl_parse(program, first_pass, self)?; - } - } - - fn parse<'src>( - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - self_name: &str, - mixin_name: &str, - ) -> Result<()> { - if let Some(mixin_data) = first_pass.mixins.get(mixin_name) { - for member in &mixin_data.members { - member.webidl_parse(program, first_pass, self_name)?; - } - } - if let Some(mixin_names) = first_pass.includes.get(mixin_name) { - for mixin_name in mixin_names { - parse(program, first_pass, self_name, mixin_name)?; - } - } - Ok(()) - } - - for member in &self.members.body { - member.webidl_parse(program, first_pass, self.identifier.0)?; - } - - parse(program, first_pass, self.identifier.0, self.identifier.0)?; - - Ok(()) - } -} - -impl<'src> WebidlParse<'src, ()> for weedle::PartialInterfaceDefinition<'src> { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - (): (), - ) -> Result<()> { - if util::is_chrome_only(&self.attributes) { - return Ok(()); - } - - if first_pass - .interfaces - .get(self.identifier.0) - .map(|interface_data| interface_data.partial) - .unwrap_or(true) { - info!( - "Partial interface {} missing non-partial interface", - self.identifier.0 - ); - } - - for member in &self.members.body { - member.webidl_parse(program, first_pass, self.identifier.0)?; - } - - Ok(()) - } -} - -impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for ExtendedAttribute<'src> { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - interface: &'src weedle::InterfaceDefinition<'src>, - ) -> Result<()> { - let mut add_constructor = |arguments: &[Argument], class: &str| { - let (overloaded, same_argument_names) = first_pass.get_operation_overloading( - arguments, - &::first_pass::OperationId::Constructor, - interface.identifier.0, - false, - ); - - let self_ty = ident_ty(rust_ident(camel_case_ident(interface.identifier.0).as_str())); - - let kind = backend::ast::ImportFunctionKind::Method { - class: class.to_string(), - ty: self_ty.clone(), - kind: backend::ast::MethodKind::Constructor, - }; - - let structural = false; - - // Constructors aren't annotated with `[Throws]` extended attributes - // (how could they be, since they themselves are extended - // attributes?) so we must conservatively assume that they can - // always throw. - // - // From https://heycam.github.io/webidl/#Constructor (emphasis - // mine): - // - // > The prose definition of a constructor must either return an IDL - // > value of a type corresponding to the interface the - // > `[Constructor]` extended attribute appears on, **or throw an - // > exception**. - let throws = true; - - for import_function in first_pass.create_function( - "new", - overloaded, - same_argument_names, - &match first_pass.convert_arguments(arguments) { - Some(arguments) => arguments, - None => return, - }, - IdlType::Interface(interface.identifier.0), - kind, - structural, - throws, - None, - ) { - program.imports.push(wrap_import_function(import_function)); - } - }; - - match self { - ExtendedAttribute::ArgList(list) - if list.identifier.0 == "Constructor" => - { - add_constructor(&list.args.body.list, interface.identifier.0) - } - ExtendedAttribute::NoArgs(other) if (other.0).0 == "Constructor" => { - add_constructor(&[], interface.identifier.0) - } - ExtendedAttribute::NamedArgList(list) - if list.lhs_identifier.0 == "NamedConstructor" => - { - add_constructor(&list.args.body.list, list.rhs_identifier.0) - } - - // these appear to be mapping to gecko preferences, seems like we - // can safely ignore - ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Pref" => {} - - // looks to be a gecko-specific attribute to tie WebIDL back to C++ - // functions perhaps - ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Func" => {} - ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "JSImplementation" => {} - ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "HeaderFile" => {} - - // Not actually mentioned in the spec and presumably a hint to - // Gecko's JS engine? Unsure, but seems like it doesn't matter to us - ExtendedAttribute::NoArgs(id) - if (id.0).0 == "ProbablyShortLivingWrapper" => {} - - // Indicates something about enumerable properties, we're not too - // interested in it - // https://heycam.github.io/webidl/#LegacyUnenumerableNamedProperties - ExtendedAttribute::NoArgs(id) - if (id.0).0 == "LegacyUnenumerableNamedProperties" => {} - - // Indicates where objects are defined (web workers and such), we - // may later want to use this for cfgs but for now we ignore it. - // https://heycam.github.io/webidl/#Exposed - ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Exposed" => {} - ExtendedAttribute::IdentList(id) if id.identifier.0 == "Exposed" => {} - - // We handle this with the "structural" attribute elsewhere - ExtendedAttribute::IdentList(id) if id.identifier.0 == "Global" => {} - - // Seems like it's safe to ignore for now, just telling us where a - // binding appears - // https://heycam.github.io/webidl/#SecureContext - ExtendedAttribute::NoArgs(id) if (id.0).0 == "SecureContext" => {} - - // We handle this elsewhere - ExtendedAttribute::NoArgs(id) if (id.0).0 == "Unforgeable" => {} - - // Looks like this attribute just says that we can't call the - // constructor - // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor - ExtendedAttribute::NoArgs(id) if (id.0).0 == "HTMLConstructor" => {} - - ExtendedAttribute::ArgList(_) - | ExtendedAttribute::Ident(_) - | ExtendedAttribute::IdentList(_) - | ExtendedAttribute::NamedArgList(_) - | ExtendedAttribute::NoArgs(_) => { - warn!("Unsupported WebIDL extended attribute: {:?}", self); - } - } - - Ok(()) - } -} - -impl<'src> WebidlParse<'src, &'src str> for weedle::interface::InterfaceMember<'src> { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - self_name: &'src str, - ) -> Result<()> { - use weedle::interface::InterfaceMember::*; - - match self { - Attribute(attr) => { - attr.webidl_parse(program, first_pass, self_name) - } - Operation(op) => { - op.webidl_parse(program, first_pass, self_name) - } - Const(const_) => { - const_.webidl_parse(program, first_pass, self_name) - } - Iterable(iterable) => { - iterable.webidl_parse(program, first_pass, self_name) - } - // TODO - Maplike(_) => { - warn!("Unsupported WebIDL Maplike interface member: {:?}", self); - Ok(()) - } - Stringifier(_) => { - warn!("Unsupported WebIDL Stringifier interface member: {:?}", self); - Ok(()) - } - Setlike(_) => { - warn!("Unsupported WebIDL Setlike interface member: {:?}", self); - Ok(()) - } - } - } -} - -impl<'a, 'src> WebidlParse<'src, &'a str> for weedle::mixin::MixinMember<'src> { - fn webidl_parse( +impl<'src> FirstPassRecord<'src> { + fn append_enum( &self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - self_name: &'a str, - ) -> Result<()> { - match self { - weedle::mixin::MixinMember::Attribute(attr) => { - attr.webidl_parse(program, first_pass, self_name) - } - weedle::mixin::MixinMember::Operation(op) => { - op.webidl_parse(program, first_pass, self_name) - } - weedle::mixin::MixinMember::Const(const_) => { - const_.webidl_parse(program, first_pass, self_name) - } - // TODO - weedle::mixin::MixinMember::Stringifier(_) => { - warn!("Unsupported WebIDL stringifier mixin member: {:?}", self); - Ok(()) - } - } - } -} - -impl<'src> WebidlParse<'src, &'src str> for weedle::interface::AttributeInterfaceMember<'src> { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - self_name: &'src str, - ) -> Result<()> { - member_attribute( - program, - first_pass, - self_name, - &self.attributes, - self.modifier, - self.readonly.is_some(), - &self.type_, - self.identifier.0, - ) - } -} - -impl<'src> WebidlParse<'src, &'src str> for weedle::mixin::AttributeMixinMember<'src> { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - self_name: &'src str, - ) -> Result<()> { - member_attribute( - program, - first_pass, - self_name, - &self.attributes, - if let Some(s) = self.stringifier { - Some(weedle::interface::StringifierOrInheritOrStatic::Stringifier(s)) - } else { - None - }, - self.readonly.is_some(), - &self.type_, - self.identifier.0, - ) - } -} - -fn member_attribute<'src>( - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - self_name: &'src str, - attrs: &'src Option, - modifier: Option, - readonly: bool, - type_: &'src weedle::types::AttributedType<'src>, - identifier: &'src str, -) -> Result<()> { - use weedle::interface::StringifierOrInheritOrStatic::*; - - if util::is_chrome_only(attrs) { - return Ok(()); - } - - let is_static = match modifier { - Some(Stringifier(_)) => { - warn!("Unsupported stringifier on type: {:?}", (self_name, identifier)); - return Ok(()) - } - Some(Inherit(_)) => false, - Some(Static(_)) => true, - None => false, - }; - - if type_.attributes.is_some() { - warn!("Unsupported attributes on type: {:?}", (self_name, identifier)); - return Ok(()) - } - - let is_structural = util::is_structural(attrs); - let throws = util::throws(attrs); - let global = first_pass - .interfaces - .get(self_name) - .map(|interface_data| interface_data.global) - .unwrap_or(false); - - for import_function in first_pass.create_getter( - identifier, - &type_.type_, - self_name, - is_static, - is_structural, - throws, - global, + enum_: &'src weedle::EnumDefinition<'src> ) { - program.imports.push(wrap_import_function(import_function)); - } - - if !readonly { - for import_function in first_pass.create_setter( - identifier, - type_.type_.clone(), - self_name, - is_static, - is_structural, - throws, - global, - ) { - program.imports.push(wrap_import_function(import_function)); - } - } - - Ok(()) -} - -impl<'src> WebidlParse<'src, &'src str> for weedle::interface::OperationInterfaceMember<'src> { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - self_name: &'src str, - ) -> Result<()> { - member_operation( - program, - first_pass, - self_name, - &self.attributes, - self.modifier, - &self.specials, - &self.return_type, - &self.args.body.list, - &self.identifier, - ) - } -} - -impl<'src> WebidlParse<'src, &'src str> for weedle::mixin::OperationMixinMember<'src> { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - self_name: &'src str, - ) -> Result<()> { - member_operation( - program, - first_pass, - self_name, - &self.attributes, - None, - &[], - &self.return_type, - &self.args.body.list, - &self.identifier, - ) - } -} - -fn member_operation<'src>( - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - self_name: &'src str, - attrs: &'src Option, - modifier: Option, - specials: &[weedle::interface::Special], - return_type: &'src weedle::types::ReturnType<'src>, - args: &'src [Argument], - identifier: &Option>, -) -> Result<()> { - use weedle::interface::StringifierOrStatic::*; - use weedle::interface::Special; - - if util::is_chrome_only(attrs) { - info!("Skipping `ChromeOnly` operation: {:?}", (self_name, identifier)); - return Ok(()); - } - - let is_static = match modifier { - Some(Stringifier(_)) => { - warn!("Unsupported stringifier on type: {:?}", (self_name, identifier)); - return Ok(()) - } - Some(Static(_)) => true, - None => false, - }; - - 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) => { - warn!("Unsupported legacy caller: {:?}", (self_name, identifier)); - return Ok(()); - }, - }; - operation_ids.push(id); - } - - let global = first_pass - .interfaces - .get(self_name) - .map(|interface_data| interface_data.global) - .unwrap_or(false); - - 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, - _ => false, - }, - util::throws(attrs), - global, - ); - - for method in methods { - program.imports.push(wrap_import_function(method)); - } - } - Ok(()) -} - -impl<'src> WebidlParse<'src, &'src str> for weedle::interface::IterableInterfaceMember<'src> { - fn webidl_parse( - &self, - _program: &mut backend::ast::Program, - _first_pass: &FirstPassRecord<'src>, - _self_name: &'src str, - ) -> Result<()> { - // if util::is_chrome_only(&self.attributes) { - // return Ok(()); - // } - -/* TODO - let throws = util::throws(&self.extended_attributes); - let return_value = weedle::ReturnType::NonVoid(self.value_type.clone()); - let args = []; - first_pass - .create_basic_method( - &args, - Some(&"values".to_string()), - &return_value, - self_name, - false, - false, // Should be false - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - - first_pass - .create_basic_method( - &args, - Some(&"keys".to_string()), - &return_value, // Should be a number - self_name, - false, - false, // Should be false - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); -*/ - - Ok(()) - } -} - -impl<'src> WebidlParse<'src, ()> for weedle::EnumDefinition<'src> { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - _: &FirstPassRecord<'src>, - (): (), - ) -> Result<()> { - if util::is_chrome_only(&self.attributes) { - return Ok(()); - } - - let variants = &self.values.body.list; + let variants = &enum_.values.body.list; program.imports.push(backend::ast::Import { module: None, js_namespace: None, kind: backend::ast::ImportKind::Enum(backend::ast::ImportEnum { vis: public(), - name: rust_ident(camel_case_ident(self.identifier.0).as_str()), + name: rust_ident(camel_case_ident(enum_.identifier.0).as_str()), variants: variants .iter() .map(|v| { @@ -826,174 +177,29 @@ impl<'src> WebidlParse<'src, ()> for weedle::EnumDefinition<'src> { rust_attrs: vec![parse_quote!(#[derive(Copy, Clone, PartialEq, Debug)])], }), }); - - Ok(()) } -} -impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> { - fn webidl_parse( - &'src self, + // tons more data for what's going on here at + // https://www.w3.org/TR/WebIDL-1/#idl-dictionaries + fn append_dictionary( + &self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - self_name: &'src str, - ) -> Result<()> { - if util::is_chrome_only(&self.attributes) { - return Ok(()); - } - - let idl_type = match self.const_type.to_idl_type(first_pass) { - None => return Ok(()), - Some(idl_type) => idl_type, + data: &first_pass::DictionaryData, + ) { + let def = match data.definition { + Some(def) => def, + None => return, }; - - let ty = match idl_type.to_syn_type(TypePosition::Return) { - None => { - warn!( - "Cannot convert const type to syn type: {:?} in {:?} on {:?}", - idl_type, - self, - self_name - ); - return Ok(()); - }, - Some(ty) => ty, - }; - - program.consts.push(backend::ast::Const { - vis: public(), - name: rust_ident(self.identifier.0.to_shouty_snake_case().as_str()), - class: Some(rust_ident(camel_case_ident(&self_name).as_str())), - ty, - value: webidl_const_v_to_backend_const_v(&self.const_value), - }); - - Ok(()) - } -} - -impl<'src> WebidlParse<'src, ()> for weedle::NamespaceDefinition<'src> { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - (): (), - ) -> Result<()> { - if util::is_chrome_only(&self.attributes) { - return Ok(()); - } - - if let Some(attrs) = &self.attributes { - for attr in &attrs.body.list { - attr.webidl_parse(program, first_pass, self)?; - } - } - - let mut module = backend::ast::Module { - vis: public(), - name: rust_ident(self.identifier.0.to_snake_case().as_str()), - imports: Default::default(), - }; - - if let Some(namespace_data) = first_pass.namespaces.get(&self.identifier.0) { - for member in &namespace_data.members { - member.webidl_parse(program, first_pass, (&self.identifier.0, &mut module))?; - } - } - - program.modules.push(module); - - Ok(()) - } -} - -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(()) - } -} - -impl<'src> WebidlParse<'src, (&'src str, &'src mut backend::ast::Module)> for weedle::namespace::NamespaceMember<'src> { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - (self_name, module): (&'src str, &mut backend::ast::Module), - ) -> Result<()> { - match self { - weedle::namespace::NamespaceMember::Operation(op) => { - op.webidl_parse(program, first_pass, (self_name, module))?; - } - weedle::namespace::NamespaceMember::Attribute(attr) => { - warn!("Unsupported attribute namespace member: {:?}", attr) - } - } - Ok(()) - } -} - -impl<'src> WebidlParse<'src, (&'src str, &'src mut backend::ast::Module)> for weedle::namespace::OperationNamespaceMember<'src> { - fn webidl_parse( - &'src self, - _program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - (self_name, module): (&'src str, &mut backend::ast::Module), - ) -> Result<()> { - if util::is_chrome_only(&self.attributes) { - return Ok(()); - } - - for import_function in first_pass.create_namespace_operation( - &self.args.body.list, - self.identifier.as_ref().map(|id| id.0), - &self.return_type, - self_name, - util::throws(&self.attributes) - ) { - module.imports.push( - backend::ast::Import { - module: None, - js_namespace: Some(raw_ident(self_name)), - kind: backend::ast::ImportKind::Function(import_function), - } - ); - }; - - Ok(()) - } -} - -// tons more data for what's going on here at -// https://www.w3.org/TR/WebIDL-1/#idl-dictionaries -impl<'src> WebidlParse<'src, ()> for weedle::DictionaryDefinition<'src> { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - (): (), - ) -> Result<()> { - if util::is_chrome_only(&self.attributes) { - return Ok(()); - } - let mut fields = Vec::new(); - if !push_members(first_pass, self.identifier.0, &mut fields) { - return Ok(()) + if !push_members(self, def.identifier.0, &mut fields) { + return } program.dictionaries.push(ast::Dictionary { - name: rust_ident(&camel_case_ident(self.identifier.0)), + name: rust_ident(&camel_case_ident(def.identifier.0)), fields, }); - return Ok(()); - fn push_members<'src>( data: &FirstPassRecord<'src>, dict: &'src str, @@ -1078,4 +284,361 @@ impl<'src> WebidlParse<'src, ()> for weedle::DictionaryDefinition<'src> { }) } } + + fn append_ns( + &'src self, + program: &mut backend::ast::Program, + name: &'src str, + ns: &'src first_pass::NamespaceData<'src>, + ) { + let mut module = backend::ast::Module { + vis: public(), + name: rust_ident(name.to_snake_case().as_str()), + imports: Default::default(), + }; + + for member in ns.members.iter() { + self.append_ns_member(&mut module, name, member); + } + + program.modules.push(module); + } + + fn append_ns_member( + &self, + module: &mut backend::ast::Module, + self_name: &'src str, + member: &'src weedle::namespace::OperationNamespaceMember<'src>, + ) { + for import_function in self.create_namespace_operation( + &member.args.body.list, + member.identifier.as_ref().map(|id| id.0), + &member.return_type, + self_name, + util::throws(&member.attributes) + ) { + module.imports.push( + backend::ast::Import { + module: None, + js_namespace: Some(raw_ident(self_name)), + kind: backend::ast::ImportKind::Function(import_function), + } + ); + } + } + + fn append_const( + &self, + program: &mut backend::ast::Program, + self_name: &'src str, + member: &'src weedle::interface::ConstMember<'src>, + ) { + let idl_type = match member.const_type.to_idl_type(self) { + Some(idl_type) => idl_type, + None => return, + }; + + let ty = match idl_type.to_syn_type(TypePosition::Return) { + Some(ty) => ty, + None => { + warn!( + "Cannot convert const type to syn type: {:?} in {:?} on {:?}", + idl_type, + member, + self_name + ); + return + }, + }; + + program.consts.push(backend::ast::Const { + vis: public(), + name: rust_ident(member.identifier.0.to_shouty_snake_case().as_str()), + class: Some(rust_ident(camel_case_ident(&self_name).as_str())), + ty, + value: webidl_const_v_to_backend_const_v(&member.const_value), + }); + } + + fn append_interface( + &self, + program: &mut backend::ast::Program, + name: &'src str, + data: &first_pass::InterfaceData, + ) { + let doc_comment = Some(format!( + "The `{}` object\n\n{}", + name, + mdn_doc(name, None), + )); + + program.imports.push(backend::ast::Import { + module: None, + js_namespace: None, + kind: backend::ast::ImportKind::Type(backend::ast::ImportType { + vis: public(), + rust_name: rust_ident(camel_case_ident(name).as_str()), + js_name: name.to_string(), + attrs: Vec::new(), + doc_comment, + instanceof_shim: format!("__widl_instanceof_{}", name), + extends: self.all_superclasses(name) + .map(|name| Ident::new(&name, Span::call_site())) + .collect(), + }), + }); + + for (ctor_name, args) in data.constructors.iter() { + self.append_constructor(program, name, ctor_name, args); + } + for member in data.methods.iter() { + self.member_operation( + program, + name, + &member.attributes, + member.modifier, + &member.specials, + &member.return_type, + &member.args.body.list, + &member.identifier, + ) + } + for member in data.consts.iter() { + self.append_const(program, name, member); + } + for member in data.attributes.iter() { + self.member_attribute( + program, + name, + &member.attributes, + member.modifier, + member.readonly.is_some(), + &member.type_, + member.identifier.0, + ); + } + + self.append_interface_mixins(program, name, name); + } + + fn append_interface_mixins( + &self, + program: &mut backend::ast::Program, + self_name: &str, + mixin_name: &str, + ) { + if let Some(mixin_data) = self.mixins.get(mixin_name) { + for member in &mixin_data.methods { + self.member_operation( + program, + self_name, + &member.attributes, + None, + &[], + &member.return_type, + &member.args.body.list, + &member.identifier, + ); + } + for member in &mixin_data.consts { + self.append_const(program, self_name, member); + } + for member in &mixin_data.attributes { + self.member_attribute( + program, + self_name, + &member.attributes, + if let Some(s) = member.stringifier { + Some(weedle::interface::StringifierOrInheritOrStatic::Stringifier(s)) + } else { + None + }, + member.readonly.is_some(), + &member.type_, + member.identifier.0, + ); + } + } + if let Some(mixin_names) = self.includes.get(mixin_name) { + for mixin_name in mixin_names { + self.append_interface_mixins(program, self_name, mixin_name); + } + } + } + + fn append_constructor( + &self, + program: &mut backend::ast::Program, + iface_name: &'src str, + ctor_name: &'src str, + args: &[Argument<'src>], + ) { + let (overloaded, same_argument_names) = self.get_operation_overloading( + args, + &::first_pass::OperationId::Constructor, + iface_name, + false, + ); + + let self_ty = ident_ty(rust_ident(camel_case_ident(iface_name).as_str())); + + let kind = backend::ast::ImportFunctionKind::Method { + class: ctor_name.to_string(), + ty: self_ty.clone(), + kind: backend::ast::MethodKind::Constructor, + }; + + let structural = false; + + // Constructors aren't annotated with `[Throws]` extended attributes + // (how could they be, since they themselves are extended + // attributes?) so we must conservatively assume that they can + // always throw. + // + // From https://heycam.github.io/webidl/#Constructor (emphasis + // mine): + // + // > The prose definition of a constructor must either return an IDL + // > value of a type corresponding to the interface the + // > `[Constructor]` extended attribute appears on, **or throw an + // > exception**. + let throws = true; + + for import_function in self.create_function( + "new", + overloaded, + same_argument_names, + &match self.convert_arguments(args) { + Some(arguments) => arguments, + None => return, + }, + IdlType::Interface(iface_name), + kind, + structural, + throws, + None, + ) { + program.imports.push(wrap_import_function(import_function)); + } + } + + fn member_attribute( + &self, + program: &mut backend::ast::Program, + self_name: &'src str, + attrs: &'src Option, + modifier: Option, + readonly: bool, + type_: &'src weedle::types::AttributedType<'src>, + identifier: &'src str, + ) { + use weedle::interface::StringifierOrInheritOrStatic::*; + + let is_static = match modifier { + Some(Stringifier(_)) => unimplemented!(), // filtered out earlier + Some(Inherit(_)) => false, + Some(Static(_)) => true, + None => false, + }; + + let is_structural = util::is_structural(attrs); + let throws = util::throws(attrs); + let global = self + .interfaces + .get(self_name) + .map(|interface_data| interface_data.global) + .unwrap_or(false); + + for import_function in self.create_getter( + identifier, + &type_.type_, + self_name, + is_static, + is_structural, + throws, + global, + ) { + program.imports.push(wrap_import_function(import_function)); + } + + if !readonly { + for import_function in self.create_setter( + identifier, + type_.type_.clone(), + self_name, + is_static, + is_structural, + throws, + global, + ) { + program.imports.push(wrap_import_function(import_function)); + } + } + } + + fn member_operation( + &self, + program: &mut backend::ast::Program, + 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>, + ) { + use weedle::interface::StringifierOrStatic::*; + use weedle::interface::Special; + + let is_static = match modifier { + Some(Stringifier(_)) => uniimplemented!(), // filtered out earlier + Some(Static(_)) => true, + None => false, + }; + + let mut operation_ids = vec![ + OperationId::Operation(identifier.map(|s| s.0)), + ]; + 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) => { + warn!("Unsupported legacy caller: {:?}", (self_name, identifier)); + return + }, + }; + operation_ids.push(id); + } + + let global = self + .interfaces + .get(self_name) + .map(|interface_data| interface_data.global) + .unwrap_or(false); + + for id in operation_ids { + let methods = self + .create_basic_method( + args, + id, + return_type, + self_name, + is_static, + match id { + OperationId::IndexingGetter | + OperationId::IndexingSetter | + OperationId::IndexingDeleter => true, + _ => false, + }, + util::throws(attrs), + global, + ); + + for method in methods { + program.imports.push(wrap_import_function(method)); + } + } + } } diff --git a/examples/julia_set/Cargo.toml b/examples/julia_set/Cargo.toml index 509b6cc8..f6b0dbdb 100644 --- a/examples/julia_set/Cargo.toml +++ b/examples/julia_set/Cargo.toml @@ -8,3 +8,4 @@ crate-type = ["cdylib"] [dependencies] wasm-bindgen = { path = "../.." } +web-sys = { path = "../../crates/web-sys" } diff --git a/examples/julia_set/src/lib.rs b/examples/julia_set/src/lib.rs index a92eea48..2d9e6ac1 100644 --- a/examples/julia_set/src/lib.rs +++ b/examples/julia_set/src/lib.rs @@ -1,18 +1,13 @@ extern crate wasm_bindgen; +extern crate web_sys; + use wasm_bindgen::prelude::*; +use web_sys::{CanvasRenderingContext2D, ImageData}; mod fractal; use fractal::get_julia_set; use fractal::complex::Complex; -#[wasm_bindgen] -extern "C" { - pub type ImageData; - - #[wasm_bindgen(constructor)] - pub fn new(arr: &Uint8ClampedArray, width: u32, height: u32) -> ImageData; -} - #[wasm_bindgen] extern "C" { pub type Uint8ClampedArray; @@ -21,19 +16,13 @@ extern "C" { pub fn new(arr: &[u8]) -> Uint8ClampedArray; } -#[wasm_bindgen] -extern "C" { - pub type CanvasRenderingContext2D; - - #[wasm_bindgen(method, js_name = putImageData)] - pub fn put_image_data(this: &CanvasRenderingContext2D, image_data: &ImageData, p_1: i32, p_2: i32); -} - #[wasm_bindgen] pub fn draw(ctx: &CanvasRenderingContext2D, width: u32, height: u32, real: f64, imaginary: f64) { let c = Complex { real, imaginary }; let data = get_julia_set(width, height, c); let uint8_array = Uint8ClampedArray::new(&data); - ctx.put_image_data(&ImageData::new(&uint8_array, width, height), 0, 0); + ctx.put_image_data( + &ImageData::with_data_and_sw_and_sh_with_sh(&data, width, height).unwrap(), + 0.0, 0.0); }