From 703b1ab91dda772f0b17a321206601ba6df70fe8 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Thu, 9 Aug 2018 19:24:33 +0300 Subject: [PATCH 01/14] Add support for unions in arguments and for optional arguments --- crates/backend/src/ast.rs | 21 +- .../enabled/WebGL2RenderingContext.webidl | 7 +- .../enabled/WebGLRenderingContext.webidl | 5 +- crates/webidl/src/first_pass.rs | 130 ++- crates/webidl/src/lib.rs | 96 ++- crates/webidl/src/util.rs | 771 ++++++++++++------ 6 files changed, 710 insertions(+), 320 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 57869a89..4f575a7b 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -7,7 +7,7 @@ use Diagnostic; /// An abstract syntax tree representing a rust program. Contains /// extra information for joining up this rust code with javascript. #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))] -#[derive(Default)] +#[derive(Default, Clone)] pub struct Program { /// rust -> js interfaces pub exports: Vec, @@ -24,6 +24,7 @@ pub struct Program { /// A rust to js interface. Allows interaction with rust objects/functions /// from javascript. #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Export { /// The javascript class name. pub class: Option, @@ -44,6 +45,7 @@ pub struct Export { /// The 3 types variations of `self`. #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub enum MethodSelf { /// `self` ByValue, @@ -54,6 +56,7 @@ pub enum MethodSelf { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Import { pub module: Option, pub js_namespace: Option, @@ -61,6 +64,7 @@ pub struct Import { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub enum ImportKind { Function(ImportFunction), Static(ImportStatic), @@ -69,6 +73,7 @@ pub enum ImportKind { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct ImportFunction { pub function: Function, pub rust_name: Ident, @@ -81,6 +86,7 @@ pub struct ImportFunction { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub enum ImportFunctionKind { Method { class: String, @@ -91,18 +97,21 @@ pub enum ImportFunctionKind { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub enum MethodKind { Constructor, Operation(Operation), } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Operation { pub is_static: bool, pub kind: OperationKind, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub enum OperationKind { Regular, Getter(Option), @@ -113,6 +122,7 @@ pub enum OperationKind { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct ImportStatic { pub vis: syn::Visibility, pub ty: syn::Type, @@ -122,6 +132,7 @@ pub struct ImportStatic { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct ImportType { pub vis: syn::Visibility, pub rust_name: Ident, @@ -133,6 +144,7 @@ pub struct ImportType { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct ImportEnum { /// The Rust enum's visibility pub vis: syn::Visibility, @@ -147,6 +159,7 @@ pub struct ImportEnum { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Function { pub name: String, pub arguments: Vec, @@ -156,6 +169,7 @@ pub struct Function { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Struct { pub name: Ident, pub fields: Vec, @@ -163,6 +177,7 @@ pub struct Struct { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct StructField { pub name: Ident, pub struct_name: Ident, @@ -174,6 +189,7 @@ pub struct StructField { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Enum { pub name: Ident, pub variants: Vec, @@ -181,6 +197,7 @@ pub struct Enum { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Variant { pub name: Ident, pub value: u32, @@ -202,6 +219,7 @@ pub enum TypeLocation { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))] +#[derive(Clone)] pub struct Const { pub vis: syn::Visibility, pub name: Ident, @@ -211,6 +229,7 @@ pub struct Const { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))] +#[derive(Clone)] /// same as webidl::ast::ConstValue pub enum ConstValue { BooleanLiteral(bool), diff --git a/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl b/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl index 336687ff..272fa5d0 100644 --- a/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl +++ b/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl @@ -31,8 +31,7 @@ interface WebGL2RenderingContext { }; -[NoInterfaceObject] -interface WebGL2RenderingContextBase +interface mixin WebGL2RenderingContextBase { const GLenum READ_BUFFER = 0x0C02; const GLenum UNPACK_ROW_LENGTH = 0x0CF2; @@ -694,8 +693,8 @@ interface WebGL2RenderingContextBase void bindVertexArray(WebGLVertexArrayObject? array); }; -WebGL2RenderingContextBase implements WebGLRenderingContextBase; -WebGL2RenderingContext implements WebGL2RenderingContextBase; +WebGL2RenderingContextBase includes WebGLRenderingContextBase; +WebGL2RenderingContext includes WebGL2RenderingContextBase; [NoInterfaceObject] interface EXT_color_buffer_float { diff --git a/crates/web-sys/webidls/enabled/WebGLRenderingContext.webidl b/crates/web-sys/webidls/enabled/WebGLRenderingContext.webidl index ea6bb5c2..df384b23 100644 --- a/crates/web-sys/webidls/enabled/WebGLRenderingContext.webidl +++ b/crates/web-sys/webidls/enabled/WebGLRenderingContext.webidl @@ -111,8 +111,7 @@ typedef (Int32Array or sequence) Int32List; // WebGL2RenderingContext have in common. This doesn't have all the things they // have in common, because we don't support splitting multiple overloads of the // same method across separate interfaces and pulling them in with "implements". -[Exposed=(Window, Worker), NoInterfaceObject] -interface WebGLRenderingContextBase { +interface mixin WebGLRenderingContextBase { /* ClearBufferMask */ const GLenum DEPTH_BUFFER_BIT = 0x00000100; const GLenum STENCIL_BUFFER_BIT = 0x00000400; @@ -802,7 +801,7 @@ interface WebGLRenderingContext { void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data); }; -WebGLRenderingContext implements WebGLRenderingContextBase; +WebGLRenderingContext includes WebGLRenderingContextBase; // For OffscreenCanvas // Reference: https://wiki.whatwg.org/wiki/OffscreenCanvas diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 4e58443a..19816707 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -26,8 +26,9 @@ pub(crate) struct FirstPassRecord<'src> { pub(crate) dictionaries: BTreeSet<&'src str>, pub(crate) enums: BTreeSet<&'src str>, /// The mixins, mapping their name to the webidl ast node for the mixin. - pub(crate) mixins: BTreeMap<&'src str, Vec<&'src MixinMembers<'src>>>, + pub(crate) mixins: BTreeMap<&'src str, MixinData<'src>>, pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>, + pub(crate) includes: BTreeMap<&'src str, BTreeSet<&'src str>>, } /// We need to collect interface data during the first pass, to be used later. @@ -40,7 +41,16 @@ pub(crate) struct InterfaceData<'src> { pub(crate) superclass: Option<&'src str>, } -#[derive(PartialEq, Eq, PartialOrd, Ord)] +/// We need to collect mixin data during the first pass, to be used later. +#[derive(Default)] +pub(crate) struct MixinData<'src> { + /// Whether only partial mixins were encountered + pub(crate) partial: bool, + pub(crate) members: Vec<&'src MixinMembers<'src>>, + pub(crate) operations: BTreeMap, OperationData<'src>>, +} + +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] pub(crate) enum OperationId<'src> { Constructor, Operation(Option<&'src str>), @@ -79,6 +89,7 @@ impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> { match self { Dictionary(dictionary) => dictionary.first_pass(record, ()), Enum(enum_) => enum_.first_pass(record, ()), + IncludesStatement(includes) => includes.first_pass(record, ()), Interface(interface) => interface.first_pass(record, ()), PartialInterface(interface) => interface.first_pass(record, ()), InterfaceMixin(mixin) => mixin.first_pass(record, ()), @@ -111,24 +122,44 @@ impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> { } } +impl<'src> FirstPass<'src, ()> for weedle::IncludesStatementDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + record + .includes + .entry(self.lhs_identifier.0) + .or_insert_with(Default::default) + .insert(self.rhs_identifier.0); + Ok(()) + } +} + fn first_pass_operation<'src>( record: &mut FirstPassRecord<'src>, + mixin: bool, self_name: &'src str, id: OperationId<'src>, arguments: &[Argument<'src>], -) -> Result<()> { +) -> Result<()> { 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(()), + Argument::Single(single) => names.push(single.identifier.0), + Argument::Variadic(variadic) => names.push(variadic.identifier.0), } } - record - .interfaces - .get_mut(self_name) - .unwrap() - .operations + if mixin { + &mut record + .mixins + .get_mut(self_name) + .unwrap() + .operations + } else { + &mut record + .interfaces + .get_mut(self_name) + .unwrap() + .operations + } .entry(id) .and_modify(|operation_data| operation_data.overloaded = true) .or_insert_with(Default::default) @@ -143,12 +174,12 @@ fn first_pass_operation<'src>( impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { { - let interface = record + let interface_data = record .interfaces .entry(self.identifier.0) .or_insert_with(Default::default); - interface.partial = false; - interface.superclass = self.inheritance.map(|s| s.identifier.0); + interface_data.partial = false; + interface_data.superclass = self.inheritance.map(|s| s.identifier.0); } if util::is_chrome_only(&self.attributes) { @@ -177,8 +208,8 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> { .or_insert_with(|| InterfaceData { partial: true, - operations: Default::default(), global: false, + operations: Default::default(), superclass: None, }, ); @@ -201,6 +232,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { ExtendedAttribute::ArgList(list) if list.identifier.0 == "Constructor" => { first_pass_operation( record, + false, self_name, OperationId::Constructor, &list.args.body.list, @@ -209,6 +241,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { ExtendedAttribute::NoArgs(name) if (name.0).0 == "Constructor" => { first_pass_operation( record, + false, self_name, OperationId::Constructor, &[], @@ -219,6 +252,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { { first_pass_operation( record, + false, self_name, OperationId::Constructor, &list.args.body.list, @@ -260,6 +294,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM } first_pass_operation( record, + false, self_name, match self.identifier.map(|s| s.0) { None => match self.specials.get(0) { @@ -278,11 +313,23 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src>{ fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { - record - .mixins - .entry(self.identifier.0) - .or_insert_with(Default::default) - .push(&self.members.body); + { + let mixin_data = record + .mixins + .entry(self.identifier.0) + .or_insert_with(Default::default); + mixin_data.partial = false; + mixin_data.members.push(&self.members.body); + } + + if util::is_chrome_only(&self.attributes) { + return Ok(()) + } + + for member in &self.members.body { + member.first_pass(record, self.identifier.0)?; + } + Ok(()) } } @@ -292,12 +339,55 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src> record .mixins .entry(self.identifier.0) - .or_insert_with(Default::default) + .or_insert_with(|| + MixinData { + partial: true, + members: Default::default(), + operations: Default::default(), + }, + ) + .members .push(&self.members.body); + + 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::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) + } + _ => Ok(()), + } + } +} + +impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { + if self.stringifier.is_some() { + warn!("Unsupported webidl operation {:?}", self); + return Ok(()) + } + first_pass_operation( + record, + true, + self_name, + OperationId::Operation(self.identifier.map(|s| s.0.clone())), + &self.args.body.list, + ) + } +} + impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { if util::is_chrome_only(&self.attributes) { diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 0fb33c90..fc76f607 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -168,9 +168,6 @@ impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> { weedle::Definition::Enum(enumeration) => { enumeration.webidl_parse(program, first_pass, ())? } - weedle::Definition::IncludesStatement(includes) => { - includes.webidl_parse(program, first_pass, ())? - } weedle::Definition::Interface(interface) => { interface.webidl_parse(program, first_pass, ())? } @@ -182,6 +179,9 @@ impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> { weedle::Definition::PartialInterfaceMixin(_) => { // handled in the first pass } + weedle::Definition::IncludesStatement(..) => { + // handled in the first pass + } weedle::Definition::Implements(..) => { // nothing to do for this, ignore it } @@ -199,25 +199,6 @@ impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> { } } -impl<'src> WebidlParse<'src, ()> for weedle::IncludesStatementDefinition<'src> { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - (): (), - ) -> Result<()> { - match first_pass.mixins.get(self.rhs_identifier.0) { - Some(member_lists) => { - for member in member_lists.iter().flat_map(|list| list.iter()) { - member.webidl_parse(program, first_pass, self.lhs_identifier.0)?; - } - } - None => warn!("Tried to include missing mixin {}", self.rhs_identifier.0), - } - Ok(()) - } -} - impl<'src> WebidlParse<'src, ()> for weedle::InterfaceDefinition<'src> { fn webidl_parse( &'src self, @@ -261,10 +242,33 @@ impl<'src> WebidlParse<'src, ()> for weedle::InterfaceDefinition<'src> { } } + 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 members in &mixin_data.members { + for member in *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(()) } } @@ -280,7 +284,11 @@ impl<'src> WebidlParse<'src, ()> for weedle::PartialInterfaceDefinition<'src> { return Ok(()); } - if !first_pass.interfaces.contains_key(self.identifier.0) { + if first_pass + .interfaces + .get(self.identifier.0) + .map(|interface_data| !interface_data.partial) + .unwrap_or(true) { warn!( "Partial interface {} missing non-partial interface", self.identifier.0 @@ -345,8 +353,11 @@ impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for Extend throws, None, ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); + .map(|import_functions| + for import_function in import_functions { + program.imports.push(wrap_import_function(import_function)); + } + ); }; match self { @@ -436,8 +447,8 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::InterfaceMember<' Operation(op) => { op.webidl_parse(program, first_pass, self_name) } - Const(cnst) => { - cnst.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) @@ -537,7 +548,7 @@ fn member_attribute<'src>( return Ok(()); } - let statik = match modifier { + let is_static = match modifier { Some(Stringifier(_)) => { warn!("Unsupported stringifier on type {:?}", (self_name, identifier)); return Ok(()) @@ -560,12 +571,15 @@ fn member_attribute<'src>( identifier, &type_.type_, self_name, - statik, + is_static, is_structural, throws, ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); + .map(|import_functions| + for import_function in import_functions { + program.imports.push(wrap_import_function(import_function)); + } + ); if !readonly { first_pass @@ -573,12 +587,15 @@ fn member_attribute<'src>( identifier, type_.type_.clone(), self_name, - statik, + is_static, is_structural, throws, ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); + .map(|import_functions| + for import_function in import_functions { + program.imports.push(wrap_import_function(import_function)); + } + ); } Ok(()) @@ -642,7 +659,7 @@ fn member_operation<'src>( if util::is_chrome_only(attrs) { return Ok(()); } - let statik = match modifier { + let is_static = match modifier { Some(Stringifier(_)) => { warn!("Unsupported stringifier on type {:?}", (self_name, identifier)); return Ok(()) @@ -670,7 +687,7 @@ fn member_operation<'src>( }, return_type, self_name, - statik, + is_static, specials.len() == 1 || first_pass .interfaces .get(self_name) @@ -678,8 +695,11 @@ fn member_operation<'src>( .unwrap_or(false), util::throws(attrs), ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); + .map(|import_functions| + for import_function in import_functions { + program.imports.push(wrap_import_function(import_function)); + } + ); Ok(()) } diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 554f90f0..b7e13342 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1,4 +1,5 @@ use std::iter::FromIterator; +use std::iter; use backend; use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident}; @@ -315,64 +316,75 @@ impl<'src> ToSynType<'src> for weedle::term::Object { } } -impl<'src> ToSynType<'src> for weedle::types::Type<'src> { +impl<'src> ToSynType<'src> for NonAnyType<'src> { fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) -> Option { - use weedle::types::NonAnyType::*; - let single = match self { - Type::Single(s) => s, - Type::Union(_) => return None, - }; + match self { + NonAnyType::Boolean(s) => s.to_syn_type(record, pos), + NonAnyType::Octet(s) => s.to_syn_type(record, pos), + NonAnyType::Byte(s) => s.to_syn_type(record, pos), + NonAnyType::Identifier(s) => s.to_syn_type(record, pos), + NonAnyType::Integer(s) => s.to_syn_type(record, pos), + NonAnyType::FloatingPoint(s) => s.to_syn_type(record, pos), - let ty = match single { - // `any` becomes `::wasm_bindgen::JsValue`. - SingleType::Any(_) => { - let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; - return Some(leading_colon_path_ty(path)) - } - SingleType::NonAny(other) => other, - }; + NonAnyType::Float32Array(s) => s.to_syn_type(record, pos), + NonAnyType::Float64Array(s) => s.to_syn_type(record, pos), + NonAnyType::Int8Array(s) => s.to_syn_type(record, pos), + NonAnyType::Int16Array(s) => s.to_syn_type(record, pos), + NonAnyType::Int32Array(s) => s.to_syn_type(record, pos), + NonAnyType::Uint8Array(s) => s.to_syn_type(record, pos), + NonAnyType::Uint8ClampedArray(s) => s.to_syn_type(record, pos), + NonAnyType::Uint16Array(s) => s.to_syn_type(record, pos), + NonAnyType::Uint32Array(s) => s.to_syn_type(record, pos), - match ty { - Boolean(s) => s.to_syn_type(record, pos), - Octet(s) => s.to_syn_type(record, pos), - Byte(s) => s.to_syn_type(record, pos), - Identifier(s) => s.to_syn_type(record, pos), - Integer(s) => s.to_syn_type(record, pos), - FloatingPoint(s) => s.to_syn_type(record, pos), - - Float32Array(s) => s.to_syn_type(record, pos), - Float64Array(s) => s.to_syn_type(record, pos), - Int8Array(s) => s.to_syn_type(record, pos), - Int16Array(s) => s.to_syn_type(record, pos), - Int32Array(s) => s.to_syn_type(record, pos), - Uint8Array(s) => s.to_syn_type(record, pos), - Uint8ClampedArray(s) => s.to_syn_type(record, pos), - Uint16Array(s) => s.to_syn_type(record, pos), - Uint32Array(s) => s.to_syn_type(record, pos), - - DOMString(s) => s.to_syn_type(record, pos), - ByteString(s) => s.to_syn_type(record, pos), - USVString(s) => s.to_syn_type(record, pos), - ArrayBuffer(b) => b.to_syn_type(record, pos), - Object(o) => o.to_syn_type(record, pos), + NonAnyType::DOMString(s) => s.to_syn_type(record, pos), + NonAnyType::ByteString(s) => s.to_syn_type(record, pos), + NonAnyType::USVString(s) => s.to_syn_type(record, pos), + NonAnyType::ArrayBuffer(b) => b.to_syn_type(record, pos), + NonAnyType::Object(o) => o.to_syn_type(record, pos), // Support for these types is not yet implemented, so skip // generating any bindings for this function. - | DataView(_) - | Error(_) - | FrozenArrayType(_) - | Promise(_) - | RecordType(..) - | Sequence(_) - | Symbol(_) => { + | NonAnyType::DataView(_) + | NonAnyType::Error(_) + | NonAnyType::FrozenArrayType(_) + | NonAnyType::Promise(_) + | NonAnyType::RecordType(..) + | NonAnyType::Sequence(_) + | NonAnyType::Symbol(_) => { None } } } } +impl<'src> ToSynType<'src> for SingleType<'src> { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + match self { + // `any` becomes `::wasm_bindgen::JsValue`. + SingleType::Any(_) => { + let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; + Some(leading_colon_path_ty(path)) + } + SingleType::NonAny(non_any) => non_any.to_syn_type(record, pos), + } + } +} + +impl<'src> ToSynType<'src> for Type<'src> { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + match self { + Type::Single(single) => single.to_syn_type(record, pos), + Type::Union(_) => None, + } + } +} + /// Map a webidl const value to the correct wasm-bindgen const value pub fn webidl_const_v_to_backend_const_v(v: &ConstValue) -> backend::ast::ConstValue { @@ -519,31 +531,153 @@ pub enum TypePosition { Return, } -/// Implemented on an AST type node to generate a snake case name. -trait TypeToString { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String); +trait GetArgumentPossibilities<'src> { + fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option>; } -impl TypeToString for MayBeNull { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { - if self.q_mark.is_some() { - dst.push_str("opt_"); - } - self.type_.type_to_string(record, dst); +impl<'src, T: GetArgumentPossibilities<'src>> GetArgumentPossibilities<'src> for MayBeNull { + fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { + Some( + self + .type_ + .get_argument_possibilities(record)? + .into_iter() + .map(|(ty, type_name)| + if self.q_mark.is_some() { + (option_ty(ty), "opt_".to_string() + &type_name) + } else { + (ty, type_name) + } + ) + .collect() + ) } } -impl<'src> TypeToString for weedle::types::ReturnType<'src> { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { +impl<'src> GetArgumentPossibilities<'src> for weedle::common::Identifier<'src> { + fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { + if let Some(other) = record.typedefs.get(&self.0) { + other.get_argument_possibilities(record) + } else { + Some( + vec![ + ( + self.to_syn_type(record, TypePosition::Argument)?, + self.get_type_name(record), + ) + ] + ) + } + } +} + +impl<'src> GetArgumentPossibilities<'src> for NonAnyType<'src> { + fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { + if let NonAnyType::Identifier(identifier) = self { + identifier.get_argument_possibilities(record) + } else { + Some( + vec![ + ( + self.to_syn_type(record, TypePosition::Argument)?, + self.get_type_name(record), + ) + ] + ) + } + } +} + +impl<'src> GetArgumentPossibilities<'src> for SingleType<'src> { + fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { + if let SingleType::NonAny(non_any) = self { + non_any.get_argument_possibilities(record) + } else { + Some( + vec![ + ( + self.to_syn_type(record, TypePosition::Argument)?, + self.get_type_name(record), + ) + ] + ) + } + } +} + +impl<'src> GetArgumentPossibilities<'src> for UnionMemberType<'src> { + fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { match self { - weedle::types::ReturnType::Type(ty) => (*ty).type_to_string(record, dst), + UnionMemberType::Single(single) => single.get_argument_possibilities(record), + UnionMemberType::Union(union) => union.get_argument_possibilities(record), + } + } +} + +impl<'src> GetArgumentPossibilities<'src> for UnionType<'src> { + fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { + let mut result = Vec::new(); + for ty in &self.body.list { + result.extend(ty.get_argument_possibilities(record)?.into_iter()); + } + Some(result) + } +} + +impl<'src> GetArgumentPossibilities<'src> for Type<'src> { + fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { + match self { + Type::Single(single) => single.get_argument_possibilities(record), + Type::Union(union) => union.get_argument_possibilities(record), + } + } +} + +/// Implemented on an AST type node to generate a snake case name. +trait GetTypeName { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String); + + fn get_type_name(&self, record: &FirstPassRecord) -> String { + let mut string = String::new(); + self.push_type_name(record, &mut string); + return string; + } +} + +impl GetTypeName for [T] { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { + let mut first = true; + for union_member_type in self { + if first { + first = false; + } else { + dst.push_str("_and_"); + } + union_member_type.push_type_name(record, dst); + } + } +} + +impl GetTypeName for MayBeNull { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { + if self.q_mark.is_some() { + dst.push_str("opt_"); + } + self.type_.push_type_name(record, dst); + } +} + +impl<'src> GetTypeName for weedle::types::ReturnType<'src> { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { + match self { + weedle::types::ReturnType::Type(ty) => (*ty).push_type_name(record, dst), weedle::types::ReturnType::Void(_) => dst.push_str("void"), } } } -impl TypeToString for weedle::types::StringType { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::types::StringType { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { match self { weedle::types::StringType::Byte(_) => dst.push_str("byte_str"), weedle::types::StringType::DOM(_) => dst.push_str("dom_str"), @@ -552,107 +686,107 @@ impl TypeToString for weedle::types::StringType { } } -impl TypeToString for weedle::term::Byte { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Byte { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("i8"); } } -impl TypeToString for weedle::term::Octet { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Octet { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("u8"); } } -impl TypeToString for weedle::term::Boolean { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Boolean { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("bool"); } } -impl TypeToString for weedle::term::USVString { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::USVString { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("usv_str"); } } -impl TypeToString for weedle::term::ByteString { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::ByteString { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("byte_str"); } } -impl TypeToString for weedle::term::DOMString { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::DOMString { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("dom_str"); } } -impl TypeToString for weedle::term::Float32Array { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Float32Array { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("f32_array"); } } -impl TypeToString for weedle::term::Float64Array { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Float64Array { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("f64_array"); } } -impl TypeToString for weedle::term::Int8Array { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Int8Array { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("i8_array"); } } -impl TypeToString for weedle::term::Int16Array { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Int16Array { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("i16_array"); } } -impl TypeToString for weedle::term::Int32Array { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Int32Array { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("i32_array"); } } -impl TypeToString for weedle::term::Uint8Array { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Uint8Array { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("u8_array"); } } -impl TypeToString for weedle::term::Uint8ClampedArray { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Uint8ClampedArray { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("u8_clamped_array"); } } -impl TypeToString for weedle::term::Uint16Array { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Uint16Array { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("u16_array"); } } -impl TypeToString for weedle::term::Uint32Array { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Uint32Array { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("u32_array"); } } -impl<'src> TypeToString for weedle::common::Identifier<'src> { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { +impl<'src> GetTypeName for weedle::common::Identifier<'src> { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { match record.typedefs.get(self.0) { - Some(other) => other.type_to_string(record, dst), + Some(other) => other.push_type_name(record, dst), None => dst.push_str(&self.0.to_snake_case()), } } } -impl TypeToString for IntegerType { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for IntegerType { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { match self { IntegerType::LongLong(l) if l.unsigned.is_some() => dst.push_str("u64"), IntegerType::LongLong(_) => dst.push_str("i64"), @@ -664,8 +798,8 @@ impl TypeToString for IntegerType { } } -impl TypeToString for FloatingPointType { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for FloatingPointType { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { match self { FloatingPointType::Float(_) => dst.push_str("f32"), FloatingPointType::Double(_) => dst.push_str("f64"), @@ -673,111 +807,133 @@ impl TypeToString for FloatingPointType { } } -impl TypeToString for weedle::term::ArrayBuffer { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::ArrayBuffer { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("array_buffer"); } } -impl TypeToString for weedle::term::Symbol { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Symbol { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("symbol"); } } -impl TypeToString for weedle::term::Object { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Object { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("object"); } } -impl TypeToString for weedle::term::DataView { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::DataView { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("data_view"); } } -impl TypeToString for weedle::term::Error { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { +impl GetTypeName for weedle::term::Error { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { dst.push_str("error"); } } -impl<'src> TypeToString for weedle::types::SequenceType<'src> { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { - dst.push_str("seq_"); - self.generics.body.type_to_string(record, dst); +impl<'src> GetTypeName for SequenceType<'src> { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { + self.generics.body.push_type_name(record, dst); + dst.push_str("_seq"); } } -impl<'src> TypeToString for weedle::types::PromiseType<'src> { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { - dst.push_str("promise_"); - self.generics.body.type_to_string(record, dst); +impl<'src> GetTypeName for PromiseType<'src> { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { + self.generics.body.push_type_name(record, dst); + dst.push_str("_promise"); } } -impl<'src> TypeToString for weedle::types::FrozenArrayType<'src> { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { - dst.push_str("frozen_array_"); - self.generics.body.type_to_string(record, dst); +impl<'src> GetTypeName for FrozenArrayType<'src> { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { + self.generics.body.push_type_name(record, dst); + dst.push_str("_frozen_array"); } } -impl<'src> TypeToString for weedle::types::RecordType<'src> { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { +impl<'src> GetTypeName for RecordType<'src> { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { dst.push_str("record_from_"); - self.generics.body.0.type_to_string(record, dst); + self.generics.body.0.push_type_name(record, dst); dst.push_str("_to_"); - self.generics.body.2.type_to_string(record, dst); + self.generics.body.2.push_type_name(record, dst); } } -impl<'a> TypeToString for weedle::types::Type<'a> { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { - use weedle::types::NonAnyType::*; +impl<'a> GetTypeName for NonAnyType<'a> { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { + match self { + NonAnyType::Boolean(s) => s.push_type_name(record, dst), + NonAnyType::Octet(s) => s.push_type_name(record, dst), + NonAnyType::Byte(s) => s.push_type_name(record, dst), + NonAnyType::Identifier(s) => s.push_type_name(record, dst), + NonAnyType::Integer(s) => s.push_type_name(record, dst), + NonAnyType::FloatingPoint(s) => s.push_type_name(record, dst), - let single = match self { - Type::Single(s) => s, - Type::Union(_) => panic!("unions not supported"), - }; + NonAnyType::Float32Array(s) => s.push_type_name(record, dst), + NonAnyType::Float64Array(s) => s.push_type_name(record, dst), + NonAnyType::Int8Array(s) => s.push_type_name(record, dst), + NonAnyType::Int16Array(s) => s.push_type_name(record, dst), + NonAnyType::Int32Array(s) => s.push_type_name(record, dst), + NonAnyType::Uint8Array(s) => s.push_type_name(record, dst), + NonAnyType::Uint8ClampedArray(s) => s.push_type_name(record, dst), + NonAnyType::Uint16Array(s) => s.push_type_name(record, dst), + NonAnyType::Uint32Array(s) => s.push_type_name(record, dst), - let ty = match single { - SingleType::Any(_) => return dst.push_str("any"), - SingleType::NonAny(other) => other, - }; + NonAnyType::DOMString(s) => s.push_type_name(record, dst), + NonAnyType::ByteString(s) => s.push_type_name(record, dst), + NonAnyType::USVString(s) => s.push_type_name(record, dst), + NonAnyType::ArrayBuffer(s) => s.push_type_name(record, dst), - match ty { - Boolean(s) => s.type_to_string(record, dst), - Octet(s) => s.type_to_string(record, dst), - Byte(s) => s.type_to_string(record, dst), - Identifier(s) => s.type_to_string(record, dst), - Integer(s) => s.type_to_string(record, dst), - FloatingPoint(s) => s.type_to_string(record, dst), + NonAnyType::DataView(s) => s.push_type_name(record, dst), + NonAnyType::Error(s) => s.push_type_name(record, dst), + NonAnyType::FrozenArrayType(s) => s.push_type_name(record, dst), + NonAnyType::Object(s) => s.push_type_name(record, dst), + NonAnyType::Promise(s) => s.push_type_name(record, dst), + NonAnyType::RecordType(s) => s.push_type_name(record, dst), + NonAnyType::Sequence(s) => s.push_type_name(record, dst), + NonAnyType::Symbol(s) => s.push_type_name(record, dst), + } + } +} - Float32Array(s) => s.type_to_string(record, dst), - Float64Array(s) => s.type_to_string(record, dst), - Int8Array(s) => s.type_to_string(record, dst), - Int16Array(s) => s.type_to_string(record, dst), - Int32Array(s) => s.type_to_string(record, dst), - Uint8Array(s) => s.type_to_string(record, dst), - Uint8ClampedArray(s) => s.type_to_string(record, dst), - Uint16Array(s) => s.type_to_string(record, dst), - Uint32Array(s) => s.type_to_string(record, dst), +impl<'a> GetTypeName for SingleType<'a> { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { + match self { + SingleType::Any(_) => dst.push_str("any"), + SingleType::NonAny(non_any) => non_any.push_type_name(record, dst), + } + } +} - DOMString(s) => s.type_to_string(record, dst), - ByteString(s) => s.type_to_string(record, dst), - USVString(s) => s.type_to_string(record, dst), - ArrayBuffer(s) => s.type_to_string(record, dst), +impl<'a> GetTypeName for UnionMemberType<'a> { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { + match self { + UnionMemberType::Single(single) => single.push_type_name(record, dst), + UnionMemberType::Union(union) => union.push_type_name(record, dst), + } + } +} - DataView(s) => s.type_to_string(record, dst), - Error(s) => s.type_to_string(record, dst), - FrozenArrayType(s) => s.type_to_string(record, dst), - Object(s) => s.type_to_string(record, dst), - Promise(s) => s.type_to_string(record, dst), - RecordType(s) => s.type_to_string(record, dst), - Sequence(s) => s.type_to_string(record, dst), - Symbol(s) => s.type_to_string(record, dst), +impl<'a> GetTypeName for UnionType<'a> { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { + dst.push_str("union_of_"); + self.body.list.push_type_name(record, dst); + } +} + +impl<'a> GetTypeName for Type<'a> { + fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { + match self { + Type::Single(single) => single.push_type_name(record, dst), + Type::Union(union) => union.push_type_name(record, dst), } } } @@ -787,46 +943,110 @@ impl<'src> FirstPassRecord<'src> { /// /// `kind` is whether the function is a method, in which case we would need a `self` /// parameter. - fn webidl_arguments_to_syn_arg_captured( + /// + /// Return option that contains a value if the conversion succeeds. + /// The value is a vector of argument variants. + /// Each variant is a vector of converted argument types and type names. + fn get_variants( &self, arguments: &[Argument], kind: &backend::ast::ImportFunctionKind, - ) -> Option> + ) -> Option)>>> { - 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(arguments.len() + 1); - res.push(simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone()))); - res - } else { - Vec::with_capacity(arguments.len()) - }; - - for argument in arguments { - let argument = match argument { - Argument::Single(arg) => arg, - Argument::Variadic(_) => return None, - }; - match argument.type_.type_.to_syn_type(self, TypePosition::Argument) { - None => { - warn!("Argument's type is not yet supported: {:?}", argument); - return None; - } - Some(ty) => { - let name = argument.identifier.0.to_snake_case(); - res.push(simple_fn_arg(rust_ident(&name), ty)) + let arguments_possibilities = { + fn get_argument_possibilities(record: &FirstPassRecord, argument: &Argument) -> Option> { + let single = match argument { + Argument::Single(single) => single, + Argument::Variadic(_) => return None, + }; + match single.type_.type_.get_argument_possibilities(record) { + None => { + warn!("Argument's type is not yet supported: {:?}", argument); + None + }, + Some(value) => Some(value), } } + if !arguments.is_empty() { + let mut optional_arguments_possibilities = Vec::new(); + if let Argument::Single(ref single) = arguments[0] { + if single.optional.is_some() { + optional_arguments_possibilities.push(Vec::new()); + } + } + let mut arguments_possibilities: Vec<_> = get_argument_possibilities( + self, + &arguments[0] + )? + .into_iter() + .map(|argument_possibility| vec![argument_possibility]) + .collect(); + for argument in arguments[1..].iter() { + let mut new_arguments_possibilities = Vec::new(); + for arguments_possibility in arguments_possibilities { + if let Argument::Single(single) = argument { + if single.optional.is_some() { + optional_arguments_possibilities.push(arguments_possibility.clone()); + } + } + let mut element_argument_possibilities = get_argument_possibilities( + self, + &argument + )?; + for element_argument_possibility in element_argument_possibilities { + new_arguments_possibilities.push( + arguments_possibility + .iter() + .cloned() + .chain(iter::once(element_argument_possibility)) + .collect() + ) + } + } + arguments_possibilities = new_arguments_possibilities + } + optional_arguments_possibilities.extend(arguments_possibilities.into_iter()); + optional_arguments_possibilities + } else { + vec![Vec::new()] + } + }; + let mut result = Vec::new(); + for arguments_possibility in arguments_possibilities { + 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(arguments.len() + 1); + res.push((simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone())), None)); + res + } else { + Vec::with_capacity(arguments.len()) + }; + for (argument, argument_possibility) in arguments.iter().zip(arguments_possibility) { + let single = match argument { + Argument::Single(single) => single, + Argument::Variadic(_) => return None, + }; + res.push( + ( + simple_fn_arg( + rust_ident(&single.identifier.0.to_snake_case()), + argument_possibility.0.clone() + ), + Some(argument_possibility.1.clone()), + ) + ); + } + result.push(res); } - Some(res) + Some(result) } /// Create a wasm-bindgen function, if possible. @@ -841,37 +1061,32 @@ impl<'src> FirstPassRecord<'src> { structural: bool, catch: bool, doc_comment: Option, - ) -> Option + ) -> Option> { - let ast_arguments = self.webidl_arguments_to_syn_arg_captured(arguments, &kind)?; - - let rust_name = rust_ident( - &if overloaded && !arguments.is_empty() { - let mut argument_type_names = String::new(); - for arg in arguments { - let arg = match arg { - Argument::Single(single) => single, - Argument::Variadic(_) => return None, - }; - if argument_type_names.len() > 0 { - argument_type_names.push_str("_and_"); - } - if same_argument_names { - arg.type_.type_.type_to_string(self, &mut argument_type_names); - } else { - argument_type_names.push_str(&arg.identifier.0.to_snake_case()); - } + let rust_name = if overloaded && !arguments.is_empty() { + let mut argument_type_names = String::new(); + for arg in arguments { + let arg = match arg { + Argument::Single(single) => single, + Argument::Variadic(_) => return None, + }; + if argument_type_names.len() > 0 { + argument_type_names.push_str("_and_"); } - if name == "new" { - "with_".to_owned() + &argument_type_names + if same_argument_names { + arg.type_.type_.push_type_name(self, &mut argument_type_names); } else { - name.to_snake_case() + "_with_" + &argument_type_names + argument_type_names.push_str(&arg.identifier.0.to_snake_case()); } - } else { - name.to_snake_case() } - ); - let name = name.to_string(); + if name == "new" { + "with_".to_owned() + &argument_type_names + } else { + name.to_snake_case() + "_with_" + &argument_type_names + } + } else { + name.to_snake_case() + }; let js_ret = ret.clone(); @@ -879,31 +1094,57 @@ impl<'src> FirstPassRecord<'src> { ret = Some(ret.map_or_else(|| result_ty(unit_ty()), result_ty)) } - let shim = { - let ns = match kind { - backend::ast::ImportFunctionKind::Normal => "", - backend::ast::ImportFunctionKind::Method { ref class, .. } => class, + let variants = self.get_variants(arguments, &kind)?; + let multiple_variants = variants.len() > 1; + let mut result = Vec::new(); + for variant in variants { + let (variant_types, variant_names): (Vec<_>, Vec<_>) = variant.into_iter().unzip(); + let rust_name = if multiple_variants { + let mut rust_name = rust_name.clone(); + let mut first = true; + for variant_name in variant_names { + if let Some(type_name) = variant_name { + if first { + rust_name.push_str("_using_"); + first = false; + } else { + rust_name.push_str("_and_"); + } + rust_name.push_str(&type_name); + } + } + rust_name + } else { + rust_name.clone() + }; + let rust_name = rust_ident(&rust_name); + 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)) }; - raw_ident(&format!("__widl_f_{}_{}", rust_name, ns)) - }; - - Some(backend::ast::ImportFunction { - function: backend::ast::Function { - name, - arguments: ast_arguments, - ret, - rust_attrs: vec![], - rust_vis: public(), - }, - rust_name, - js_ret, - catch, - structural, - kind, - shim, - doc_comment, - }) + result.push(backend::ast::ImportFunction { + function: backend::ast::Function { + name: name.to_string(), + arguments: variant_types, + ret: ret.clone(), + rust_attrs: vec![], + rust_vis: public(), + }, + rust_name, + js_ret: js_ret.clone(), + catch, + structural, + kind: kind.clone(), + shim, + doc_comment: doc_comment.clone(), + }) + } + Some(result) } /// Create a wasm-bindgen method, if possible. @@ -916,7 +1157,7 @@ impl<'src> FirstPassRecord<'src> { is_static: bool, structural: bool, catch: bool, - ) -> Option { + ) -> Option> { let (overloaded, same_argument_names) = self.get_operation_overloading( arguments, &operation_id, @@ -999,14 +1240,36 @@ impl<'src> FirstPassRecord<'src> { id: &::first_pass::OperationId, self_name: &str, ) -> (bool, bool) { - let data = match self.interfaces.get(self_name) { - Some(data) => data, - None => return (false, false), - }; - let data = match data.operations.get(id) { - Some(data) => data, - None => return (false, false), - }; + fn get_operation_data<'src>( + record: &'src FirstPassRecord, + id: &'src ::first_pass::OperationId, + self_name: &str, + mixin_name: &str, + ) -> Option<&'src ::first_pass::OperationData<'src>> { + if let Some(mixin_data) = record.mixins.get(mixin_name) { + if let Some(operation_data) = mixin_data.operations.get(id) { + return Some(operation_data); + } + } + if let Some(mixin_names) = record.includes.get(mixin_name) { + for mixin_name in mixin_names { + if let Some(operation_data) = get_operation_data(record, id, self_name, mixin_name) { + return Some(operation_data); + } + } + } + None + } + + let operation_data = self + .interfaces + .get(self_name) + .and_then(|interface_data| interface_data.operations.get(id)) + .unwrap_or_else(|| + get_operation_data(self, id, self_name, self_name) + .expect(&format!("not found operation {:?} in interface {}", id, self_name)) + ); + let mut names = Vec::with_capacity(arguments.len()); for arg in arguments { match arg { @@ -1015,8 +1278,8 @@ impl<'src> FirstPassRecord<'src> { } } ( - data.overloaded, - *data + operation_data.overloaded, + *operation_data .argument_names_same .get(&names) .unwrap_or(&false) @@ -1032,7 +1295,7 @@ impl<'src> FirstPassRecord<'src> { is_static: bool, is_structural: bool, catch: bool, - ) -> Option { + ) -> Option> { let ret = match ty.to_syn_type(self, TypePosition::Return) { None => { warn!("Attribute's type does not yet support reading: {:?}", ty); @@ -1063,7 +1326,7 @@ impl<'src> FirstPassRecord<'src> { is_static: bool, is_structural: bool, catch: bool, - ) -> Option { + ) -> Option> { let kind = backend::ast::ImportFunctionKind::Method { class: self_name.to_string(), ty: ident_ty(rust_ident(camel_case_ident(&self_name).as_str())), From 131f2232413435d9fc2ddaaf134c2df305b66dc2 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Thu, 9 Aug 2018 21:51:41 +0300 Subject: [PATCH 02/14] Use argument names instead of argument type names if possible --- crates/webidl/src/util.rs | 63 +++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index b7e13342..aadc602e 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1,5 +1,6 @@ use std::iter::FromIterator; use std::iter; +use std::collections::BTreeMap; use backend; use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident}; @@ -946,12 +947,12 @@ impl<'src> FirstPassRecord<'src> { /// /// Return option that contains a value if the conversion succeeds. /// The value is a vector of argument variants. - /// Each variant is a vector of converted argument types and type names. + /// Each variant is a vector of original arguments, converted argument types and type names. fn get_variants( &self, - arguments: &[Argument], + arguments: &'src [Argument], kind: &backend::ast::ImportFunctionKind, - ) -> Option)>>> + ) -> Option, (syn::ArgCaptured, Option))>>> { let arguments_possibilities = { fn get_argument_possibilities(record: &FirstPassRecord, argument: &Argument) -> Option> { @@ -1023,7 +1024,15 @@ impl<'src> FirstPassRecord<'src> { .. } = kind { let mut res = Vec::with_capacity(arguments.len() + 1); - res.push((simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone())), None)); + res.push( + ( + None, + ( + simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone())), + None, + ), + ) + ); res } else { Vec::with_capacity(arguments.len()) @@ -1035,11 +1044,14 @@ impl<'src> FirstPassRecord<'src> { }; res.push( ( - simple_fn_arg( - rust_ident(&single.identifier.0.to_snake_case()), - argument_possibility.0.clone() + Some(argument), + ( + simple_fn_arg( + rust_ident(&single.identifier.0.to_snake_case()), + argument_possibility.0.clone() + ), + Some(argument_possibility.1.clone()), ), - Some(argument_possibility.1.clone()), ) ); } @@ -1065,8 +1077,8 @@ impl<'src> FirstPassRecord<'src> { { let rust_name = if overloaded && !arguments.is_empty() { let mut argument_type_names = String::new(); - for arg in arguments { - let arg = match arg { + for argument in arguments { + let argument = match argument { Argument::Single(single) => single, Argument::Variadic(_) => return None, }; @@ -1074,9 +1086,9 @@ impl<'src> FirstPassRecord<'src> { argument_type_names.push_str("_and_"); } if same_argument_names { - arg.type_.type_.push_type_name(self, &mut argument_type_names); + argument.type_.type_.push_type_name(self, &mut argument_type_names); } else { - argument_type_names.push_str(&arg.identifier.0.to_snake_case()); + argument_type_names.push_str(&argument.identifier.0.to_snake_case()); } } if name == "new" { @@ -1096,13 +1108,22 @@ impl<'src> FirstPassRecord<'src> { let variants = self.get_variants(arguments, &kind)?; let multiple_variants = variants.len() > 1; + let mut arguments_count_variants_count = BTreeMap::new(); + for variant in &variants { + arguments_count_variants_count + .entry(variant.len()) + .and_modify(|variants_count| { *variants_count += 1usize; }) + .or_insert(1usize); + } let mut result = Vec::new(); for variant in variants { - let (variant_types, variant_names): (Vec<_>, Vec<_>) = variant.into_iter().unzip(); + let (arguments, variant_types_variant_names): (Vec<_>, Vec<(_, _)>) = variant.into_iter().unzip(); + let (variant_types, variant_names): (Vec<_>, Vec<_>) = variant_types_variant_names.into_iter().unzip(); + let variant_names_len = variant_names.len(); let rust_name = if multiple_variants { let mut rust_name = rust_name.clone(); let mut first = true; - for variant_name in variant_names { + for (argument, variant_name) in arguments.iter().zip(variant_names) { if let Some(type_name) = variant_name { if first { rust_name.push_str("_using_"); @@ -1110,7 +1131,19 @@ impl<'src> FirstPassRecord<'src> { } else { rust_name.push_str("_and_"); } - rust_name.push_str(&type_name); + if arguments_count_variants_count[&variant_names_len] == 1 { + if let Some(argument) = argument { + let argument = match argument { + Argument::Single(single) => single, + Argument::Variadic(_) => return None, + }; + rust_name.push_str(&argument.identifier.0.to_snake_case()); + } else { + rust_name.push_str(&type_name); + } + } else { + rust_name.push_str(&type_name); + } } } rust_name From 5127dd3f959f1365b41509246359fd5c7fb7a3fe Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Thu, 9 Aug 2018 22:13:50 +0300 Subject: [PATCH 03/14] Fix tests --- crates/web-sys/tests/wasm/dom_point.rs | 4 ++-- crates/web-sys/tests/wasm/option_element.rs | 2 +- crates/web-sys/tests/wasm/table_element.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/web-sys/tests/wasm/dom_point.rs b/crates/web-sys/tests/wasm/dom_point.rs index 61441bf1..f1e87f3d 100644 --- a/crates/web-sys/tests/wasm/dom_point.rs +++ b/crates/web-sys/tests/wasm/dom_point.rs @@ -4,7 +4,7 @@ use web_sys::{DomPoint, DomPointReadOnly}; #[wasm_bindgen_test] fn dom_point() { - let x = DomPoint::new(1.0, 2.0, 3.0, 4.0).unwrap(); + let x = DomPoint::new_using_x_and_y_and_z_and_w(1.0, 2.0, 3.0, 4.0).unwrap(); assert_eq!(x.x(), 1.0); x.set_x(1.5); assert_eq!(x.x(), 1.5); @@ -24,7 +24,7 @@ fn dom_point() { #[wasm_bindgen_test] fn dom_point_readonly() { - let x = DomPoint::new(1.0, 2.0, 3.0, 4.0).unwrap(); + let x = DomPoint::new_using_x_and_y_and_z_and_w(1.0, 2.0, 3.0, 4.0).unwrap(); let x = DomPointReadOnly::from(JsValue::from(x)); assert_eq!(x.x(), 1.0); assert_eq!(x.y(), 2.0); diff --git a/crates/web-sys/tests/wasm/option_element.rs b/crates/web-sys/tests/wasm/option_element.rs index dbd85a91..e2e074e7 100644 --- a/crates/web-sys/tests/wasm/option_element.rs +++ b/crates/web-sys/tests/wasm/option_element.rs @@ -3,7 +3,7 @@ use web_sys::HtmlOptionElement; #[wasm_bindgen_test] fn test_option_element() { - let option = HtmlOptionElement::new( + let option = HtmlOptionElement::new_using_text_and_value_and_default_selected_and_selected( "option_text", "option_value", false, diff --git a/crates/web-sys/tests/wasm/table_element.rs b/crates/web-sys/tests/wasm/table_element.rs index 98e04b74..c83ae53e 100644 --- a/crates/web-sys/tests/wasm/table_element.rs +++ b/crates/web-sys/tests/wasm/table_element.rs @@ -99,7 +99,7 @@ fn test_table_element() { ); table - .insert_row(0) + .insert_row_using_index(0) .expect("Failed to insert row at index 0"); assert!( table.rows().length() == 1, From d9d8f761f158544bc34ba74fa0a97575238e99d2 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Fri, 10 Aug 2018 00:52:12 +0300 Subject: [PATCH 04/14] Add Exposed --- crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl b/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl index 272fa5d0..95ba590a 100644 --- a/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl +++ b/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl @@ -26,7 +26,8 @@ typedef (Uint32Array or sequence) Uint32List; // WebGL2 spec has this as an empty interface that pulls in everything // via WebGL2RenderingContextBase. -[Pref="webgl.enable-webgl2"] +[Exposed=(Window,Worker), + Pref="webgl.enable-webgl2"] interface WebGL2RenderingContext { }; From b6ba5cf4f19e21b83a58d793017eed8f34ff807e Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Fri, 10 Aug 2018 00:55:27 +0300 Subject: [PATCH 05/14] Replace unwrap with expect --- crates/webidl/src/first_pass.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 19816707..8aa677df 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -151,13 +151,13 @@ fn first_pass_operation<'src>( &mut record .mixins .get_mut(self_name) - .unwrap() + .expect(&format!("not found {} mixin", self_name)) .operations } else { &mut record .interfaces .get_mut(self_name) - .unwrap() + .expect(&format!("not found {} interface", self_name)) .operations } .entry(id) From e2af5639c58d752f3cec778e858fcf310214b968 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Fri, 10 Aug 2018 01:12:12 +0300 Subject: [PATCH 06/14] Add documentation for GetArgumentPossibilities --- crates/webidl/src/util.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index aadc602e..93e778e4 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -532,7 +532,13 @@ pub enum TypePosition { Return, } +/// Implemented on an AST type node to get equivalent list of syn types and type names that do not have unions. +/// For example, it turns `Promise<(sequence or short)>` into +/// corresponding syn types and type names of `[Promise>, short]`. trait GetArgumentPossibilities<'src> { + /// Returns option that contains a value if the conversion succeeds. + /// The value is a vector of argument possibilities. + /// Each possibility is a tuple of converted argument (syn) types and type names fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option>; } @@ -945,7 +951,7 @@ impl<'src> FirstPassRecord<'src> { /// `kind` is whether the function is a method, in which case we would need a `self` /// parameter. /// - /// Return option that contains a value if the conversion succeeds. + /// Returns option that contains a value if the conversion succeeds. /// The value is a vector of argument variants. /// Each variant is a vector of original arguments, converted argument types and type names. fn get_variants( From 22f92af124c9bb6fef5d38a18b9e565d68be4d6f Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Fri, 10 Aug 2018 15:54:19 +0300 Subject: [PATCH 07/14] Fix example in docs --- crates/webidl/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 93e778e4..ba1a5e23 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -534,7 +534,7 @@ pub enum TypePosition { /// Implemented on an AST type node to get equivalent list of syn types and type names that do not have unions. /// For example, it turns `Promise<(sequence or short)>` into -/// corresponding syn types and type names of `[Promise>, short]`. +/// corresponding syn types and type names of `[Promise>, Promise]`. trait GetArgumentPossibilities<'src> { /// Returns option that contains a value if the conversion succeeds. /// The value is a vector of argument possibilities. From 90f172a34feceba3ef2d2badbad2d3d447031651 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Fri, 10 Aug 2018 15:56:57 +0300 Subject: [PATCH 08/14] Fix too much newlines --- crates/webidl/src/util.rs | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index ba1a5e23..ae0aa168 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -566,14 +566,9 @@ impl<'src> GetArgumentPossibilities<'src> for weedle::common::Identifier<'src> { if let Some(other) = record.typedefs.get(&self.0) { other.get_argument_possibilities(record) } else { - Some( - vec![ - ( - self.to_syn_type(record, TypePosition::Argument)?, - self.get_type_name(record), - ) - ] - ) + let syn_type = self.to_syn_type(record, TypePosition::Argument)?; + let type_name = self.get_type_name(record); + Some(vec![(syn_type, type_name)]) } } } @@ -583,14 +578,9 @@ impl<'src> GetArgumentPossibilities<'src> for NonAnyType<'src> { if let NonAnyType::Identifier(identifier) = self { identifier.get_argument_possibilities(record) } else { - Some( - vec![ - ( - self.to_syn_type(record, TypePosition::Argument)?, - self.get_type_name(record), - ) - ] - ) + let syn_type = self.to_syn_type(record, TypePosition::Argument)?; + let type_name = self.get_type_name(record); + Some(vec![(syn_type, type_name)]) } } } @@ -600,14 +590,9 @@ impl<'src> GetArgumentPossibilities<'src> for SingleType<'src> { if let SingleType::NonAny(non_any) = self { non_any.get_argument_possibilities(record) } else { - Some( - vec![ - ( - self.to_syn_type(record, TypePosition::Argument)?, - self.get_type_name(record), - ) - ] - ) + let syn_type = self.to_syn_type(record, TypePosition::Argument)?; + let type_name = self.get_type_name(record); + Some(vec![(syn_type, type_name)]) } } } From 5ae18d18e9d415c49ede78708e1f4ad2e20f723f Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Fri, 10 Aug 2018 16:40:35 +0300 Subject: [PATCH 09/14] Add macro for implementing simple type names --- crates/webidl/src/util.rs | 153 ++++++++------------------------------ 1 file changed, 33 insertions(+), 120 deletions(-) diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index ae0aa168..27a99d9c 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -659,6 +659,39 @@ impl GetTypeName for MayBeNull { } } +macro_rules! term_type_names { + ($($t:tt => $r:tt)*) => ($( + impl GetTypeName for weedle::term::$t { + fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str($r); + } + } + )*) +} + +term_type_names!( + Boolean => "bool" + Byte => "i8" + Octet => "u8" + Int8Array => "i8_array" + Uint8Array => "u8_array" + Uint8ClampedArray => "u8_clamped_array" + Int16Array => "i16_array" + Uint16Array => "u16_array" + Int32Array => "i32_array" + Uint32Array => "u32_array" + Float32Array => "f32_array" + Float64Array => "f64_array" + USVString => "usv_str" + ByteString => "byte_str" + DOMString => "dom_str" + ArrayBuffer => "array_buffer" + Symbol => "symbol" + Object => "object" + DataView => "data_view" + Error => "error" +); + impl<'src> GetTypeName for weedle::types::ReturnType<'src> { fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { match self { @@ -678,96 +711,6 @@ impl GetTypeName for weedle::types::StringType { } } -impl GetTypeName for weedle::term::Byte { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("i8"); - } -} - -impl GetTypeName for weedle::term::Octet { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("u8"); - } -} - -impl GetTypeName for weedle::term::Boolean { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("bool"); - } -} - -impl GetTypeName for weedle::term::USVString { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("usv_str"); - } -} - -impl GetTypeName for weedle::term::ByteString { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("byte_str"); - } -} - -impl GetTypeName for weedle::term::DOMString { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("dom_str"); - } -} - -impl GetTypeName for weedle::term::Float32Array { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("f32_array"); - } -} - -impl GetTypeName for weedle::term::Float64Array { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("f64_array"); - } -} - -impl GetTypeName for weedle::term::Int8Array { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("i8_array"); - } -} - -impl GetTypeName for weedle::term::Int16Array { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("i16_array"); - } -} - -impl GetTypeName for weedle::term::Int32Array { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("i32_array"); - } -} - -impl GetTypeName for weedle::term::Uint8Array { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("u8_array"); - } -} - -impl GetTypeName for weedle::term::Uint8ClampedArray { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("u8_clamped_array"); - } -} - -impl GetTypeName for weedle::term::Uint16Array { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("u16_array"); - } -} - -impl GetTypeName for weedle::term::Uint32Array { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("u32_array"); - } -} - impl<'src> GetTypeName for weedle::common::Identifier<'src> { fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { match record.typedefs.get(self.0) { @@ -799,36 +742,6 @@ impl GetTypeName for FloatingPointType { } } -impl GetTypeName for weedle::term::ArrayBuffer { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("array_buffer"); - } -} - -impl GetTypeName for weedle::term::Symbol { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("symbol"); - } -} - -impl GetTypeName for weedle::term::Object { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("object"); - } -} - -impl GetTypeName for weedle::term::DataView { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("data_view"); - } -} - -impl GetTypeName for weedle::term::Error { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str("error"); - } -} - impl<'src> GetTypeName for SequenceType<'src> { fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { self.generics.body.push_type_name(record, dst); From ce2c0ded74f20fee1ba02b8bf9dddca6bbc6f33e Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Fri, 10 Aug 2018 17:19:40 +0300 Subject: [PATCH 10/14] Fix chrome only checks --- crates/webidl/src/first_pass.rs | 54 +++++++++++++++++++++++---------- crates/webidl/src/lib.rs | 9 ++++++ 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 8aa677df..6d2e434a 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -105,15 +105,24 @@ 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(()); + } + if !record.dictionaries.insert(self.identifier.0) { warn!("encountered multiple dictionary declarations of {}", self.identifier.0); } + Ok(()) } } impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + if !record.enums.insert(self.identifier.0) { warn!("Encountered multiple enum declarations of {}", self.identifier.0); } @@ -124,11 +133,16 @@ impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> { impl<'src> FirstPass<'src, ()> for weedle::IncludesStatementDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + record .includes .entry(self.lhs_identifier.0) .or_insert_with(Default::default) .insert(self.rhs_identifier.0); + Ok(()) } } @@ -173,6 +187,10 @@ fn first_pass_operation<'src>( impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + { let interface_data = record .interfaces @@ -182,10 +200,6 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { interface_data.superclass = self.inheritance.map(|s| s.identifier.0); } - if util::is_chrome_only(&self.attributes) { - return Ok(()) - } - if let Some(attrs) = &self.attributes { for attr in &attrs.body.list { attr.first_pass(record, self.identifier.0)?; @@ -202,6 +216,10 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { 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) @@ -214,10 +232,6 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> { }, ); - if util::is_chrome_only(&self.attributes) { - return Ok(()) - } - for member in &self.members.body { member.first_pass(record, self.identifier.0)?; } @@ -284,6 +298,10 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::InterfaceMember<'sr impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceMember<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + if !self.specials.is_empty() && self.specials.len() != 1 { warn!("Unsupported webidl operation {:?}", self); return Ok(()) @@ -313,6 +331,10 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src>{ fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + { let mixin_data = record .mixins @@ -322,10 +344,6 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src>{ mixin_data.members.push(&self.members.body); } - if util::is_chrome_only(&self.attributes) { - return Ok(()) - } - for member in &self.members.body { member.first_pass(record, self.identifier.0)?; } @@ -336,6 +354,10 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src>{ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + record .mixins .entry(self.identifier.0) @@ -349,10 +371,6 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src> .members .push(&self.members.body); - if util::is_chrome_only(&self.attributes) { - return Ok(()) - } - for member in &self.members.body { member.first_pass(record, self.identifier.0)?; } @@ -374,6 +392,10 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::MixinMember<'src> { impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + if self.stringifier.is_some() { warn!("Unsupported webidl operation {:?}", self); return Ok(()) diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index fc76f607..1471dc46 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -659,6 +659,7 @@ fn member_operation<'src>( if util::is_chrome_only(attrs) { return Ok(()); } + let is_static = match modifier { Some(Stringifier(_)) => { warn!("Unsupported stringifier on type {:?}", (self_name, identifier)); @@ -754,6 +755,10 @@ impl<'src> WebidlParse<'src, ()> for weedle::EnumDefinition<'src> { _: &FirstPassRecord<'src>, (): (), ) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + let variants = &self.values.body.list; program.imports.push(backend::ast::Import { module: None, @@ -787,6 +792,10 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> record: &FirstPassRecord<'src>, self_name: &'src str, ) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + let ty = match self.const_type.to_syn_type(record, TypePosition::Return) { Some(s) => s, None => return Ok(()), From 2c0e13a03311b48d475635fa8da426a298c5160c Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Fri, 10 Aug 2018 22:17:29 +0300 Subject: [PATCH 11/14] Move get_arguments_possibilities into a function --- crates/webidl/src/util.rs | 162 +++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 82 deletions(-) diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 27a99d9c..80d19216 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -844,78 +844,84 @@ impl<'a> GetTypeName for Type<'a> { } impl<'src> FirstPassRecord<'src> { - /// Use the first pass to convert webidl function arguments to rust arguments. + /// Uses the first pass to convert webidl function arguments to all possible argument combinations. + /// Returns option that contains a value if the conversion succeeds. + /// The value is a vector of argument possibilities. + /// Each possibility is a vector of tuples of converted argument (syn) types and type names + fn get_arguments_possibilities(&self, arguments: &'src [Argument]) -> Option>> { + fn get_argument_possibilities(record: &FirstPassRecord, argument: &Argument) -> Option> { + let type_ = match argument { + Argument::Single(single) => &single.type_.type_, + Argument::Variadic(variadic) => &variadic.type_, + }; + match type_.get_argument_possibilities(record) { + None => { + warn!("Argument's type is not yet supported: {:?}", argument); + None + }, + Some(value) => Some(value), + } + } + if !arguments.is_empty() { + let mut optional_arguments_possibilities = Vec::new(); + if let Argument::Single(ref single) = arguments[0] { + if single.optional.is_some() { + optional_arguments_possibilities.push(Vec::new()); + } + } + let mut arguments_possibilities: Vec<_> = get_argument_possibilities( + self, + &arguments[0] + )? + .into_iter() + .map(|argument_possibility| vec![argument_possibility]) + .collect(); + for argument in arguments[1..].iter() { + let mut new_arguments_possibilities = Vec::new(); + for arguments_possibility in arguments_possibilities { + if let Argument::Single(single) = argument { + if single.optional.is_some() { + optional_arguments_possibilities.push(arguments_possibility.clone()); + } + } + let mut element_argument_possibilities = get_argument_possibilities( + self, + &argument + )?; + for element_argument_possibility in element_argument_possibilities { + new_arguments_possibilities.push( + arguments_possibility + .iter() + .cloned() + .chain(iter::once(element_argument_possibility)) + .collect() + ) + } + } + arguments_possibilities = new_arguments_possibilities + } + optional_arguments_possibilities.extend(arguments_possibilities.into_iter()); + Some(optional_arguments_possibilities) + } else { + Some(vec![Vec::new()]) + } + } + + /// Uses the first pass to convert webidl function arguments to rust arguments. /// /// `kind` is whether the function is a method, in which case we would need a `self` /// parameter. /// /// Returns option that contains a value if the conversion succeeds. /// The value is a vector of argument variants. - /// Each variant is a vector of original arguments, converted argument types and type names. + /// Each variant is a vector of tuples of original arguments, converted argument types and type names. fn get_variants( &self, arguments: &'src [Argument], kind: &backend::ast::ImportFunctionKind, - ) -> Option, (syn::ArgCaptured, Option))>>> + ) -> Option, syn::ArgCaptured, Option)>>> { - let arguments_possibilities = { - fn get_argument_possibilities(record: &FirstPassRecord, argument: &Argument) -> Option> { - let single = match argument { - Argument::Single(single) => single, - Argument::Variadic(_) => return None, - }; - match single.type_.type_.get_argument_possibilities(record) { - None => { - warn!("Argument's type is not yet supported: {:?}", argument); - None - }, - Some(value) => Some(value), - } - } - if !arguments.is_empty() { - let mut optional_arguments_possibilities = Vec::new(); - if let Argument::Single(ref single) = arguments[0] { - if single.optional.is_some() { - optional_arguments_possibilities.push(Vec::new()); - } - } - let mut arguments_possibilities: Vec<_> = get_argument_possibilities( - self, - &arguments[0] - )? - .into_iter() - .map(|argument_possibility| vec![argument_possibility]) - .collect(); - for argument in arguments[1..].iter() { - let mut new_arguments_possibilities = Vec::new(); - for arguments_possibility in arguments_possibilities { - if let Argument::Single(single) = argument { - if single.optional.is_some() { - optional_arguments_possibilities.push(arguments_possibility.clone()); - } - } - let mut element_argument_possibilities = get_argument_possibilities( - self, - &argument - )?; - for element_argument_possibility in element_argument_possibilities { - new_arguments_possibilities.push( - arguments_possibility - .iter() - .cloned() - .chain(iter::once(element_argument_possibility)) - .collect() - ) - } - } - arguments_possibilities = new_arguments_possibilities - } - optional_arguments_possibilities.extend(arguments_possibilities.into_iter()); - optional_arguments_possibilities - } else { - vec![Vec::new()] - } - }; + let arguments_possibilities = self.get_arguments_possibilities(arguments)?; let mut result = Vec::new(); for arguments_possibility in arguments_possibilities { let mut res = if let backend::ast::ImportFunctionKind::Method { @@ -931,11 +937,9 @@ impl<'src> FirstPassRecord<'src> { res.push( ( None, - ( - simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone())), - None, - ), - ) + simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone())), + None, + ), ); res } else { @@ -949,14 +953,12 @@ impl<'src> FirstPassRecord<'src> { res.push( ( Some(argument), - ( - simple_fn_arg( - rust_ident(&single.identifier.0.to_snake_case()), - argument_possibility.0.clone() - ), - Some(argument_possibility.1.clone()), + simple_fn_arg( + rust_ident(&single.identifier.0.to_snake_case()), + argument_possibility.0.clone() ), - ) + Some(argument_possibility.1.clone()), + ), ); } result.push(res); @@ -1011,7 +1013,6 @@ impl<'src> FirstPassRecord<'src> { } let variants = self.get_variants(arguments, &kind)?; - let multiple_variants = variants.len() > 1; let mut arguments_count_variants_count = BTreeMap::new(); for variant in &variants { arguments_count_variants_count @@ -1020,14 +1021,11 @@ impl<'src> FirstPassRecord<'src> { .or_insert(1usize); } let mut result = Vec::new(); - for variant in variants { - let (arguments, variant_types_variant_names): (Vec<_>, Vec<(_, _)>) = variant.into_iter().unzip(); - let (variant_types, variant_names): (Vec<_>, Vec<_>) = variant_types_variant_names.into_iter().unzip(); - let variant_names_len = variant_names.len(); - let rust_name = if multiple_variants { + for variant in &variants { + let rust_name = if variants.len() > 1 { let mut rust_name = rust_name.clone(); let mut first = true; - for (argument, variant_name) in arguments.iter().zip(variant_names) { + for (argument, _, variant_name) in variant { if let Some(type_name) = variant_name { if first { rust_name.push_str("_using_"); @@ -1035,7 +1033,7 @@ impl<'src> FirstPassRecord<'src> { } else { rust_name.push_str("_and_"); } - if arguments_count_variants_count[&variant_names_len] == 1 { + if arguments_count_variants_count[&variant.len()] == 1 { if let Some(argument) = argument { let argument = match argument { Argument::Single(single) => single, @@ -1067,7 +1065,7 @@ impl<'src> FirstPassRecord<'src> { result.push(backend::ast::ImportFunction { function: backend::ast::Function { name: name.to_string(), - arguments: variant_types, + arguments: variant.iter().map(|variant| variant.1.clone()).collect(), ret: ret.clone(), rust_attrs: vec![], rust_vis: public(), From e92374a8c8f73777538bdf25a804ae5581ec0bb0 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Sat, 11 Aug 2018 23:46:33 +0300 Subject: [PATCH 12/14] Migrate to IdlType --- crates/webidl/src/idl_type.rs | 675 +++++++++++++++++++++++ crates/webidl/src/lib.rs | 137 +++-- crates/webidl/src/util.rs | 988 +++++----------------------------- 3 files changed, 871 insertions(+), 929 deletions(-) create mode 100644 crates/webidl/src/idl_type.rs diff --git a/crates/webidl/src/idl_type.rs b/crates/webidl/src/idl_type.rs new file mode 100644 index 00000000..7d4cd81e --- /dev/null +++ b/crates/webidl/src/idl_type.rs @@ -0,0 +1,675 @@ +use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident}; +use heck::SnakeCase; +use syn; +use weedle::common::Identifier; +use weedle::term; +use weedle::types::*; + +use first_pass::FirstPassRecord; +use util::{TypePosition, camel_case_ident, shared_ref, option_ty, array}; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] +pub(crate) enum IdlType<'a> { + Boolean, + Byte, + Octet, + Short, + UnsignedShort, + Long, + UnsignedLong, + LongLong, + UnsignedLongLong, + Float, + UnrestrictedFloat, + Double, + UnrestrictedDouble, + DomString, + ByteString, + UsvString, + Object, + Symbol, + Error, + + ArrayBuffer, + DataView, + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + + Interface(&'a str), + Dictionary(&'a str), + Enum(&'a str), + + Nullable(Box>), + FrozenArray(Box>), + Sequence(Box>), + Promise(Box>), + Record(Box>, Box>), + Union(Vec>), + + Any, + Void, +} + +pub(crate) trait ToIdlType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option>; +} + +impl<'a> ToIdlType<'a> for UnionType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + let mut idl_types = Vec::with_capacity(self.body.list.len()); + for t in &self.body.list { + idl_types.push(t.to_idl_type(record)?); + } + Some(IdlType::Union(idl_types)) + } +} + +impl<'a> ToIdlType<'a> for Type<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + Type::Single(t) => t.to_idl_type(record), + Type::Union(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for SingleType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + SingleType::Any(t) => t.to_idl_type(record), + SingleType::NonAny(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for NonAnyType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + NonAnyType::Promise(t) => t.to_idl_type(record), + NonAnyType::Integer(t) => t.to_idl_type(record), + NonAnyType::FloatingPoint(t) => t.to_idl_type(record), + NonAnyType::Boolean(t) => t.to_idl_type(record), + NonAnyType::Byte(t) => t.to_idl_type(record), + NonAnyType::Octet(t) => t.to_idl_type(record), + NonAnyType::ByteString(t) => t.to_idl_type(record), + NonAnyType::DOMString(t) => t.to_idl_type(record), + NonAnyType::USVString(t) => t.to_idl_type(record), + NonAnyType::Sequence(t) => t.to_idl_type(record), + NonAnyType::Object(t) => t.to_idl_type(record), + NonAnyType::Symbol(t) => t.to_idl_type(record), + NonAnyType::Error(t) => t.to_idl_type(record), + NonAnyType::ArrayBuffer(t) => t.to_idl_type(record), + NonAnyType::DataView(t) => t.to_idl_type(record), + NonAnyType::Int8Array(t) => t.to_idl_type(record), + NonAnyType::Int16Array(t) => t.to_idl_type(record), + NonAnyType::Int32Array(t) => t.to_idl_type(record), + NonAnyType::Uint8Array(t) => t.to_idl_type(record), + NonAnyType::Uint16Array(t) => t.to_idl_type(record), + NonAnyType::Uint32Array(t) => t.to_idl_type(record), + NonAnyType::Uint8ClampedArray(t) => t.to_idl_type(record), + NonAnyType::Float32Array(t) => t.to_idl_type(record), + NonAnyType::Float64Array(t) => t.to_idl_type(record), + NonAnyType::FrozenArrayType(t) => t.to_idl_type(record), + NonAnyType::RecordType(t) => t.to_idl_type(record), + NonAnyType::Identifier(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for SequenceType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + Some(IdlType::Sequence(Box::new(self.generics.body.to_idl_type(record)?))) + } +} + +impl<'a> ToIdlType<'a> for FrozenArrayType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + Some(IdlType::FrozenArray(Box::new(self.generics.body.to_idl_type(record)?))) + } +} + +impl<'a, T: ToIdlType<'a>> ToIdlType<'a> for MayBeNull { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + let inner_idl_type = self.type_.to_idl_type(record)?; + if self.q_mark.is_some() { + Some(IdlType::Nullable(Box::new(inner_idl_type))) + } else { + Some(inner_idl_type) + } + } +} + +impl<'a> ToIdlType<'a> for PromiseType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + Some(IdlType::Promise(Box::new(self.generics.body.to_idl_type(record)?))) + } +} + +impl<'a> ToIdlType<'a> for IntegerType { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + IntegerType::LongLong(t) => t.to_idl_type(record), + IntegerType::Long(t) => t.to_idl_type(record), + IntegerType::Short(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for LongLongType { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + if self.unsigned.is_some() { + Some(IdlType::UnsignedLongLong) + } else { + Some(IdlType::LongLong) + } + } +} + +impl<'a> ToIdlType<'a> for LongType { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + if self.unsigned.is_some() { + Some(IdlType::UnsignedLong) + } else { + Some(IdlType::Long) + } + } +} + +impl<'a> ToIdlType<'a> for ShortType { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + if self.unsigned.is_some() { + Some(IdlType::UnsignedShort) + } else { + Some(IdlType::Short) + } + } +} + +impl<'a> ToIdlType<'a> for FloatingPointType { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + FloatingPointType::Float(t) => t.to_idl_type(record), + FloatingPointType::Double(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for FloatType { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + if self.unrestricted.is_some() { + Some(IdlType::UnrestrictedFloat) + } else { + Some(IdlType::Float) + } + } +} + +impl<'a> ToIdlType<'a> for DoubleType { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + if self.unrestricted.is_some() { + Some(IdlType::UnrestrictedDouble) + } else { + Some(IdlType::Double) + } + } +} + +impl<'a> ToIdlType<'a> for RecordType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + Some( + IdlType::Record( + Box::new(self.generics.body.0.to_idl_type(record)?), + Box::new(self.generics.body.2.to_idl_type(record)?) + ) + ) + } +} + +impl<'a> ToIdlType<'a> for StringType { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + StringType::Byte(t) => t.to_idl_type(record), + StringType::DOM(t) => t.to_idl_type(record), + StringType::USV(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for UnionMemberType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + UnionMemberType::Single(t) => t.to_idl_type(record), + UnionMemberType::Union(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for ConstType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + ConstType::Integer(t) => t.to_idl_type(record), + ConstType::FloatingPoint(t) => t.to_idl_type(record), + ConstType::Boolean(t) => t.to_idl_type(record), + ConstType::Byte(t) => t.to_idl_type(record), + ConstType::Octet(t) => t.to_idl_type(record), + ConstType::Identifier(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for ReturnType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + ReturnType::Void(t) => t.to_idl_type(record), + ReturnType::Type(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for AttributedType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + self.type_.to_idl_type(record) + } +} + +impl<'a> ToIdlType<'a> for Identifier<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + if let Some(idl_type) = record.typedefs.get(&self.0) { + idl_type.to_idl_type(record) + } else if record.interfaces.contains_key(self.0) { + Some(IdlType::Interface(self.0)) + } else if record.dictionaries.contains(self.0) { + Some(IdlType::Dictionary(self.0)) + } else if record.enums.contains(self.0) { + Some(IdlType::Enum(self.0)) + } else { + warn!("unrecognized type {}", self.0); + None + } + } +} + +macro_rules! terms_to_idl_type { + ($($t:tt => $r:tt)*) => ($( + impl<'a> ToIdlType<'a> for term::$t { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + Some(IdlType::$r) + } + } + )*) +} + +terms_to_idl_type! { + Symbol => Symbol + ByteString => ByteString + DOMString => DomString + USVString => UsvString + Any => Any + Boolean => Boolean + Byte => Byte + Double => Double + Float => Float + Long => Long + Object => Object + Octet => Octet + Short => Short + Void => Void + ArrayBuffer => ArrayBuffer + DataView => DataView + Int8Array => Int8Array + Int16Array => Int16Array + Int32Array => Int32Array + Uint8Array => Uint8Array + Uint16Array => Uint16Array + Uint32Array => Uint32Array + Uint8ClampedArray => Uint8ClampedArray + Float32Array => Float32Array + Float64Array => Float64Array + Error => Error +} + +impl<'a> IdlType<'a> { + /// Generates a snake case type name. + pub(crate) fn push_type_name(&self, dst: &mut String) { + match self { + IdlType::Boolean => dst.push_str("bool"), + IdlType::Byte => dst.push_str("i8"), + IdlType::Octet => dst.push_str("u8"), + IdlType::Short => dst.push_str("i16"), + IdlType::UnsignedShort => dst.push_str("u16"), + IdlType::Long => dst.push_str("i32"), + IdlType::UnsignedLong => dst.push_str("u32"), + IdlType::LongLong => dst.push_str("i64"), + IdlType::UnsignedLongLong => dst.push_str("u64"), + IdlType::Float => dst.push_str("f32"), + IdlType::UnrestrictedFloat => dst.push_str("unrestricted_f32"), + IdlType::Double => dst.push_str("f64"), + IdlType::UnrestrictedDouble => dst.push_str("unrestricted_f64"), + IdlType::DomString => dst.push_str("dom_str"), + IdlType::ByteString => dst.push_str("byte_str"), + IdlType::UsvString => dst.push_str("usv_str"), + IdlType::Object => dst.push_str("object"), + IdlType::Symbol => dst.push_str("symbol"), + IdlType::Error => dst.push_str("error"), + + IdlType::ArrayBuffer => dst.push_str("array_buffer"), + IdlType::DataView => dst.push_str("data_view"), + IdlType::Int8Array => dst.push_str("i8_array"), + IdlType::Uint8Array => dst.push_str("u8_array"), + IdlType::Uint8ClampedArray => dst.push_str("u8_clamped_array"), + IdlType::Int16Array => dst.push_str("i16_array"), + IdlType::Uint16Array => dst.push_str("u16_array"), + IdlType::Int32Array => dst.push_str("i32_array"), + IdlType::Uint32Array => dst.push_str("u32_array"), + IdlType::Float32Array => dst.push_str("f32_array"), + IdlType::Float64Array => dst.push_str("f64_array"), + + IdlType::Interface(name) => dst.push_str(&name.to_snake_case()), + IdlType::Dictionary(name) => dst.push_str(&name.to_snake_case()), + IdlType::Enum(name) => dst.push_str(&name.to_snake_case()), + + IdlType::Nullable(idl_type) => { + dst.push_str("opt_"); + idl_type.push_type_name(dst); + }, + IdlType::FrozenArray(idl_type) => { + idl_type.push_type_name(dst); + dst.push_str("_frozen_array"); + }, + IdlType::Sequence(idl_type) => { + idl_type.push_type_name(dst); + dst.push_str("_sequence"); + }, + IdlType::Promise(idl_type) => { + idl_type.push_type_name(dst); + dst.push_str("_promise"); + }, + IdlType::Record(idl_type_from, idl_type_to) => { + dst.push_str("record_from_"); + idl_type_from.push_type_name(dst); + dst.push_str("_to_"); + idl_type_to.push_type_name(dst); + }, + IdlType::Union(idl_types) => { + dst.push_str("union_of_"); + let mut first = true; + for idl_type in idl_types { + if first { + first = false; + } else { + dst.push_str("_and_"); + } + idl_type.push_type_name(dst); + } + }, + + IdlType::Any => dst.push_str("any"), + IdlType::Void => dst.push_str("void"), + } + } + + /// Generates a snake case type name. + #[allow(dead_code)] + pub(crate) fn get_type_name(&self) -> String { + let mut string = String::new(); + self.push_type_name(&mut string); + return string; + } + + /// Converts to syn type if possible. + pub(crate) fn to_syn_type(&self, pos: TypePosition) -> Option { + match self { + IdlType::Boolean => Some(ident_ty(raw_ident("bool"))), + IdlType::Byte => Some(ident_ty(raw_ident("i8"))), + IdlType::Octet => Some(ident_ty(raw_ident("u8"))), + IdlType::Short => Some(ident_ty(raw_ident("i16"))), + IdlType::UnsignedShort => Some(ident_ty(raw_ident("u16"))), + IdlType::Long => Some(ident_ty(raw_ident("i32"))), + IdlType::UnsignedLong => Some(ident_ty(raw_ident("u32"))), + IdlType::LongLong => Some(ident_ty(raw_ident("i64"))), + IdlType::UnsignedLongLong => Some(ident_ty(raw_ident("u64"))), + IdlType::Float => Some(ident_ty(raw_ident("f32"))), + IdlType::UnrestrictedFloat => Some(ident_ty(raw_ident("f32"))), + IdlType::Double => Some(ident_ty(raw_ident("f64"))), + IdlType::UnrestrictedDouble => Some(ident_ty(raw_ident("f64"))), + | IdlType::DomString + | IdlType::ByteString + | IdlType::UsvString => match pos { + TypePosition::Argument => Some(shared_ref(ident_ty(raw_ident("str")))), + TypePosition::Return => Some(ident_ty(raw_ident("String"))), + }, + IdlType::Object => { + let path = vec![rust_ident("js_sys"), rust_ident("Object")]; + Some(leading_colon_path_ty(path)) + }, + IdlType::Symbol => None, + IdlType::Error => None, + + IdlType::ArrayBuffer => { + let path = vec![rust_ident("js_sys"), rust_ident("ArrayBuffer")]; + Some(leading_colon_path_ty(path)) + }, + IdlType::DataView => None, + IdlType::Int8Array => Some(array("i8", pos)), + IdlType::Uint8Array => Some(array("u8", pos)), + IdlType::Uint8ClampedArray => Some(array("u8", pos)), + IdlType::Int16Array => Some(array("i16", pos)), + IdlType::Uint16Array => Some(array("u16", pos)), + IdlType::Int32Array => Some(array("i32", pos)), + IdlType::Uint32Array => Some(array("u32", pos)), + IdlType::Float32Array => Some(array("f32", pos)), + IdlType::Float64Array => Some(array("f64", pos)), + + IdlType::Interface(name) => { + let ty = ident_ty(rust_ident(camel_case_ident(name).as_str())); + if pos == TypePosition::Argument { + Some(shared_ref(ty)) + } else { + Some(ty) + } + }, + IdlType::Dictionary(name) => Some(ident_ty(rust_ident(camel_case_ident(name).as_str()))), + IdlType::Enum(name) => Some(ident_ty(rust_ident(camel_case_ident(name).as_str()))), + + IdlType::Nullable(idl_type) => Some(option_ty(idl_type.to_syn_type(pos)?)), + IdlType::FrozenArray(_idl_type) => None, + IdlType::Sequence(_idl_type) => None, + IdlType::Promise(_idl_type) => None, + IdlType::Record(_idl_type_from, _idl_type_to) => None, + IdlType::Union(_idl_types) => None, + + IdlType::Any => { + let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; + Some(leading_colon_path_ty(path)) + }, + IdlType::Void => None, + } + } + + /// Flattens unions recursively. + /// + /// Works similarly to [flattened union member types], + /// but also flattens unions inside generics of other types. + /// + /// [flattened union member types]: https://heycam.github.io/webidl/#dfn-flattened-union-member-types + pub(crate) fn flatten(&self) -> Vec { + match self { + IdlType::Nullable(idl_type) => idl_type + .flatten() + .into_iter() + .map(Box::new) + .map(IdlType::Nullable) + .collect(), + IdlType::FrozenArray(idl_type) => idl_type + .flatten() + .into_iter() + .map(Box::new) + .map(IdlType::FrozenArray) + .collect(), + IdlType::Sequence(idl_type) => idl_type + .flatten() + .into_iter() + .map(Box::new) + .map(IdlType::Sequence) + .collect(), + IdlType::Promise(idl_type) => idl_type + .flatten() + .into_iter() + .map(Box::new) + .map(IdlType::Promise) + .collect(), + IdlType::Record(idl_type_from, idl_type_to) => { + let mut idl_types = Vec::new(); + for idl_type_from in idl_type_from.flatten() { + for idl_type_to in idl_type_to.flatten() { + idl_types.push( + IdlType::Record( + Box::new(idl_type_from.clone()), + Box::new(idl_type_to.clone()) + ) + ); + } + } + idl_types + }, + IdlType::Union(idl_types) => idl_types + .iter() + .flat_map(|idl_type| idl_type.flatten()) + .collect(), + + idl_type @ _ => vec![idl_type.clone()] + } + } +} + +#[test] +fn idl_type_flatten_test() { + use self::IdlType::*; + + assert_eq!( + Union(vec![ + Interface("Node"), + Union(vec![ + Sequence( + Box::new(Long), + ), + Interface("Event"), + ]), + Nullable( + Box::new(Union(vec![ + Interface("XMLHttpRequest"), + DomString, + ])), + ), + Sequence( + Box::new(Union(vec![ + Sequence( + Box::new(Double), + ), + Interface("NodeList"), + ])), + ), + ]).flatten(), + vec![ + Interface("Node"), + Sequence(Box::new(Long)), + Interface("Event"), + Nullable(Box::new(Interface("XMLHttpRequest"))), + Nullable(Box::new(DomString)), + Sequence(Box::new(Sequence(Box::new(Double)))), + Sequence(Box::new(Interface("NodeList"))), + ], + ); +} + +/// Converts arguments into possibilities. +/// +/// Each argument represented with a tuple of its idl type and whether it is optional. +/// Each possibility is a vector of idl types. +/// +/// The goal is to find equivalent possibilities of argument types each of which is not optional and +/// does not contains union types. +pub(crate) fn flatten<'a>(arguments: &'a [(IdlType, bool)]) -> Vec>> { + if arguments.is_empty() { + return vec![Vec::new()]; + } + let mut optional_possibilities = if arguments[0].1 { vec![Vec::new()] } else { Vec::new() }; + let mut possibilities = Vec::new(); + for idl_type in arguments[0].0.flatten() { + possibilities.push(vec![idl_type]) + } + for argument in arguments[1..].iter() { + let mut new_possibilities = Vec::new(); + for old_idl_types in possibilities { + if argument.1 { + optional_possibilities.push(old_idl_types.clone()); + } + for idl_type in argument.0.flatten() { + let mut new_idl_types = old_idl_types.clone(); + new_idl_types.push(idl_type); + new_possibilities.push(new_idl_types) + } + } + possibilities = new_possibilities; + } + optional_possibilities.extend(possibilities.into_iter()); + optional_possibilities +} + +#[test] +fn arguments_flatten_test() { + use self::IdlType::*; + + assert_eq!( + flatten( + &vec![ + ( + Union(vec![ + Short, + Long, + ]), + false, + ), + ( + Union(vec![ + Sequence(Box::new( + Union(vec![ + Byte, + Octet, + ]), + )), + LongLong, + ]), + true, + ), + ( + DomString, + true, + ) + ] + ), + vec![ + vec![Short], + vec![Long], + vec![Short, Sequence(Box::new(Byte))], + vec![Short, Sequence(Box::new(Octet))], + vec![Short, LongLong], + vec![Long, Sequence(Box::new(Byte))], + vec![Long, Sequence(Box::new(Octet))], + vec![Long, LongLong], + vec![Short, Sequence(Box::new(Byte)), DomString], + vec![Short, Sequence(Box::new(Octet)), DomString], + vec![Short, LongLong, DomString], + vec![Long, Sequence(Box::new(Byte)), DomString], + vec![Long, Sequence(Box::new(Octet)), DomString], + vec![Long, LongLong, DomString] + ], + ); +} diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 1471dc46..fc1f2f0e 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -25,6 +25,7 @@ extern crate wasm_bindgen_backend as backend; extern crate weedle; mod first_pass; +mod idl_type; mod util; mod error; @@ -45,7 +46,7 @@ use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList}; use first_pass::{FirstPass, FirstPassRecord}; use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc}; -use util::ToSynType; +use idl_type::{IdlType, ToIdlType}; pub use error::{Error, ErrorKind, Result}; @@ -341,23 +342,22 @@ impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for Extend // > exception**. let throws = true; - first_pass - .create_function( - "new", - overloaded, - same_argument_names, - arguments, - Some(self_ty), - kind, - structural, - throws, - None, - ) - .map(|import_functions| - for import_function in import_functions { - program.imports.push(wrap_import_function(import_function)); - } - ); + for import_function in first_pass.create_function( + "new", + overloaded, + same_argument_names, + &match first_pass.convert_arguments(arguments) { + None => return, + Some(arguments) => arguments + }, + IdlType::Interface(interface.identifier.0), + kind, + structural, + throws, + None, + ) { + program.imports.push(wrap_import_function(import_function)); + } }; match self { @@ -566,36 +566,28 @@ fn member_attribute<'src>( let is_structural = util::is_structural(attrs); let throws = util::throws(attrs); - first_pass - .create_getter( + for import_function in first_pass.create_getter( identifier, &type_.type_, self_name, is_static, is_structural, throws, - ) - .map(|import_functions| - for import_function in import_functions { - program.imports.push(wrap_import_function(import_function)); - } - ); + ) { + program.imports.push(wrap_import_function(import_function)); + } if !readonly { - first_pass - .create_setter( - identifier, - type_.type_.clone(), - self_name, - is_static, - is_structural, - throws, - ) - .map(|import_functions| - for import_function in import_functions { - program.imports.push(wrap_import_function(import_function)); - } - ); + for import_function in first_pass.create_setter( + identifier, + type_.type_.clone(), + self_name, + is_static, + is_structural, + throws, + ) { + program.imports.push(wrap_import_function(import_function)); + } } Ok(()) @@ -669,38 +661,34 @@ fn member_operation<'src>( 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(()) - } + 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(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(()), }, - 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), - ) - .map(|import_functions| - for import_function in import_functions { - program.imports.push(wrap_import_function(import_function)); + 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)); + } Ok(()) } @@ -796,9 +784,14 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> return Ok(()); } - let ty = match self.const_type.to_syn_type(record, TypePosition::Return) { - Some(s) => s, + let idl_type = match self.const_type.to_idl_type(record) { None => return Ok(()), + Some(idl_type) => idl_type, + }; + + let ty = match idl_type.to_syn_type(TypePosition::Return) { + None => return Ok(()), + Some(ty) => ty, }; program.consts.push(backend::ast::Const { diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 80d19216..d94c3e01 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1,5 +1,4 @@ use std::iter::FromIterator; -use std::iter; use std::collections::BTreeMap; use backend; @@ -9,15 +8,14 @@ use proc_macro2::Ident; use syn; use weedle; use weedle::attribute::{ExtendedAttributeList, ExtendedAttribute}; -use weedle::argument::{Argument, SingleArgument}; -use weedle::common::Identifier; -use weedle::types::*; +use weedle::argument::Argument; use weedle::literal::{ConstValue, FloatLit, IntegerLit}; use first_pass::FirstPassRecord; +use idl_type::{IdlType, ToIdlType, flatten}; /// Take a type and create an immutable shared reference to that type. -fn shared_ref(ty: syn::Type) -> syn::Type { +pub(crate) fn shared_ref(ty: syn::Type) -> syn::Type { syn::TypeReference { and_token: Default::default(), lifetime: None, @@ -40,183 +38,8 @@ pub fn mdn_doc(class: &str, method: Option<&str>) -> String { format!("[Documentation]({})", link).into() } -pub(crate) trait ToSynType<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option; -} - -impl<'src, T: ToSynType<'src>> ToSynType<'src> for MayBeNull { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - let ty = self.type_.to_syn_type(record, pos)?; - if self.q_mark.is_some() { - Some(option_ty(ty)) - } else { - Some(ty) - } - } -} - -impl<'src> ToSynType<'src> for ConstType<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - match self { - ConstType::Integer(l) => l.to_syn_type(record, pos), - ConstType::FloatingPoint(l) => l.to_syn_type(record, pos), - ConstType::Boolean(l) => l.to_syn_type(record, pos), - ConstType::Byte(l) => l.to_syn_type(record, pos), - ConstType::Octet(l) => l.to_syn_type(record, pos), - ConstType::Identifier(l) => l.to_syn_type(record, pos), - } - } -} - -impl<'src> ToSynType<'src> for IntegerType { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - match self { - IntegerType::LongLong(l) => l.to_syn_type(record, pos), - IntegerType::Long(l) => l.to_syn_type(record, pos), - IntegerType::Short(l) => l.to_syn_type(record, pos), - } - } -} - -impl<'src> ToSynType<'src> for ShortType { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - if self.unsigned.is_some() { - Some(ident_ty(raw_ident("u16"))) - } else { - Some(ident_ty(raw_ident("i16"))) - } - } -} - -impl<'src> ToSynType<'src> for LongType { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - if self.unsigned.is_some() { - Some(ident_ty(raw_ident("u32"))) - } else { - Some(ident_ty(raw_ident("i32"))) - } - } -} - -impl<'src> ToSynType<'src> for LongLongType { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - if self.unsigned.is_some() { - Some(ident_ty(raw_ident("u64"))) - } else { - Some(ident_ty(raw_ident("i64"))) - } - } -} - -impl<'src> ToSynType<'src> for FloatingPointType { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - match self { - FloatingPointType::Float(_) => Some(ident_ty(raw_ident("f32"))), - FloatingPointType::Double(_) => Some(ident_ty(raw_ident("f64"))), - } - } -} - -impl<'src> ToSynType<'src> for weedle::term::Boolean { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - Some(ident_ty(raw_ident("bool"))) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Byte { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - Some(ident_ty(raw_ident("i8"))) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Octet { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - Some(ident_ty(raw_ident("u8"))) - } -} - -impl<'src> ToSynType<'src> for weedle::common::Identifier<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - if let Some(other) = record.typedefs.get(&self.0) { - return other.to_syn_type(record, pos) - } - // A reference to a type by name becomes the same thing in the - // bindings. - let ty = ident_ty(rust_ident(camel_case_ident(self.0).as_str())); - Some(if record.interfaces.contains_key(self.0) { - if pos == TypePosition::Argument { - shared_ref(ty) - } else { - ty - } - } else if record.dictionaries.contains(self.0) { - ty - } else if record.enums.contains(self.0) { - ty - } else { - warn!("unrecognized type {}", self.0); - ty - }) - } -} - -impl<'src> ToSynType<'src> for weedle::term::DOMString { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - // strings -> `&str` for arguments and `String` for return - Some(match pos { - TypePosition::Argument => shared_ref(ident_ty(raw_ident("str"))), - TypePosition::Return => ident_ty(raw_ident("String")), - }) - } -} - -impl<'src> ToSynType<'src> for weedle::term::ByteString { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - // ByteString maps to String in JS - - // https://developer.mozilla.org/en-US/docs/Web/API/ByteString - weedle::term::DOMString.to_syn_type(record, pos) - } -} - -impl<'src> ToSynType<'src> for weedle::term::USVString { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - // USVString maps to String in JS - - // https://developer.mozilla.org/en-US/docs/Web/API/USVString - weedle::term::DOMString.to_syn_type(record, pos) - } -} - // Array type is borrowed for arguments (`&[T]`) and owned for return value (`Vec`). -fn array(base_ty: &str, pos: TypePosition) -> syn::Type { +pub(crate) fn array(base_ty: &str, pos: TypePosition) -> syn::Type { match pos { TypePosition::Argument => { shared_ref(slice_ty(ident_ty(raw_ident(base_ty)))) @@ -227,166 +50,6 @@ fn array(base_ty: &str, pos: TypePosition) -> syn::Type { } } -impl<'src> ToSynType<'src> for weedle::term::Float32Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("f32", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Float64Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("f64", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Int8Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("i8", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Int16Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("i16", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Int32Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("i32", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Uint8Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("u8", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Uint8ClampedArray { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("u8", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Uint16Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("u16", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Uint32Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("u32", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::ArrayBuffer { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - let path = vec![rust_ident("js_sys"), rust_ident("ArrayBuffer")]; - Some(leading_colon_path_ty(path)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Object { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - let path = vec![rust_ident("js_sys"), rust_ident("Object")]; - Some(leading_colon_path_ty(path)) - } -} - -impl<'src> ToSynType<'src> for NonAnyType<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - match self { - NonAnyType::Boolean(s) => s.to_syn_type(record, pos), - NonAnyType::Octet(s) => s.to_syn_type(record, pos), - NonAnyType::Byte(s) => s.to_syn_type(record, pos), - NonAnyType::Identifier(s) => s.to_syn_type(record, pos), - NonAnyType::Integer(s) => s.to_syn_type(record, pos), - NonAnyType::FloatingPoint(s) => s.to_syn_type(record, pos), - - NonAnyType::Float32Array(s) => s.to_syn_type(record, pos), - NonAnyType::Float64Array(s) => s.to_syn_type(record, pos), - NonAnyType::Int8Array(s) => s.to_syn_type(record, pos), - NonAnyType::Int16Array(s) => s.to_syn_type(record, pos), - NonAnyType::Int32Array(s) => s.to_syn_type(record, pos), - NonAnyType::Uint8Array(s) => s.to_syn_type(record, pos), - NonAnyType::Uint8ClampedArray(s) => s.to_syn_type(record, pos), - NonAnyType::Uint16Array(s) => s.to_syn_type(record, pos), - NonAnyType::Uint32Array(s) => s.to_syn_type(record, pos), - - NonAnyType::DOMString(s) => s.to_syn_type(record, pos), - NonAnyType::ByteString(s) => s.to_syn_type(record, pos), - NonAnyType::USVString(s) => s.to_syn_type(record, pos), - NonAnyType::ArrayBuffer(b) => b.to_syn_type(record, pos), - NonAnyType::Object(o) => o.to_syn_type(record, pos), - - // Support for these types is not yet implemented, so skip - // generating any bindings for this function. - | NonAnyType::DataView(_) - | NonAnyType::Error(_) - | NonAnyType::FrozenArrayType(_) - | NonAnyType::Promise(_) - | NonAnyType::RecordType(..) - | NonAnyType::Sequence(_) - | NonAnyType::Symbol(_) => { - None - } - } - } -} - -impl<'src> ToSynType<'src> for SingleType<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - match self { - // `any` becomes `::wasm_bindgen::JsValue`. - SingleType::Any(_) => { - let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; - Some(leading_colon_path_ty(path)) - } - SingleType::NonAny(non_any) => non_any.to_syn_type(record, pos), - } - } -} - -impl<'src> ToSynType<'src> for Type<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - match self { - Type::Single(single) => single.to_syn_type(record, pos), - Type::Union(_) => None, - } - } -} - - /// Map a webidl const value to the correct wasm-bindgen const value pub fn webidl_const_v_to_backend_const_v(v: &ConstValue) -> backend::ast::ConstValue { use std::f64::{NEG_INFINITY, INFINITY, NAN}; @@ -484,7 +147,7 @@ fn result_ty(t: syn::Type) -> syn::Type { } /// From `T` create `[T]`. -fn slice_ty(t: syn::Type) -> syn::Type { +pub(crate) fn slice_ty(t: syn::Type) -> syn::Type { syn::TypeSlice { bracket_token: Default::default(), elem: Box::new(t), @@ -492,7 +155,7 @@ fn slice_ty(t: syn::Type) -> syn::Type { } /// From `T` create `Vec`. -fn vec_ty(t: syn::Type) -> syn::Type { +pub(crate) fn vec_ty(t: syn::Type) -> syn::Type { let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { colon2_token: None, lt_token: Default::default(), @@ -510,7 +173,7 @@ fn vec_ty(t: syn::Type) -> syn::Type { } /// From `T` create `Option` -fn option_ty(t: syn::Type) -> syn::Type { +pub(crate) fn option_ty(t: syn::Type) -> syn::Type { let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { colon2_token: None, lt_token: Default::default(), @@ -532,469 +195,30 @@ pub enum TypePosition { Return, } -/// Implemented on an AST type node to get equivalent list of syn types and type names that do not have unions. -/// For example, it turns `Promise<(sequence or short)>` into -/// corresponding syn types and type names of `[Promise>, Promise]`. -trait GetArgumentPossibilities<'src> { - /// Returns option that contains a value if the conversion succeeds. - /// The value is a vector of argument possibilities. - /// Each possibility is a tuple of converted argument (syn) types and type names - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option>; -} - -impl<'src, T: GetArgumentPossibilities<'src>> GetArgumentPossibilities<'src> for MayBeNull { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - Some( - self - .type_ - .get_argument_possibilities(record)? - .into_iter() - .map(|(ty, type_name)| - if self.q_mark.is_some() { - (option_ty(ty), "opt_".to_string() + &type_name) - } else { - (ty, type_name) - } - ) - .collect() - ) - } -} - -impl<'src> GetArgumentPossibilities<'src> for weedle::common::Identifier<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - if let Some(other) = record.typedefs.get(&self.0) { - other.get_argument_possibilities(record) - } else { - let syn_type = self.to_syn_type(record, TypePosition::Argument)?; - let type_name = self.get_type_name(record); - Some(vec![(syn_type, type_name)]) - } - } -} - -impl<'src> GetArgumentPossibilities<'src> for NonAnyType<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - if let NonAnyType::Identifier(identifier) = self { - identifier.get_argument_possibilities(record) - } else { - let syn_type = self.to_syn_type(record, TypePosition::Argument)?; - let type_name = self.get_type_name(record); - Some(vec![(syn_type, type_name)]) - } - } -} - -impl<'src> GetArgumentPossibilities<'src> for SingleType<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - if let SingleType::NonAny(non_any) = self { - non_any.get_argument_possibilities(record) - } else { - let syn_type = self.to_syn_type(record, TypePosition::Argument)?; - let type_name = self.get_type_name(record); - Some(vec![(syn_type, type_name)]) - } - } -} - -impl<'src> GetArgumentPossibilities<'src> for UnionMemberType<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - match self { - UnionMemberType::Single(single) => single.get_argument_possibilities(record), - UnionMemberType::Union(union) => union.get_argument_possibilities(record), - } - } -} - -impl<'src> GetArgumentPossibilities<'src> for UnionType<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - let mut result = Vec::new(); - for ty in &self.body.list { - result.extend(ty.get_argument_possibilities(record)?.into_iter()); - } - Some(result) - } -} - -impl<'src> GetArgumentPossibilities<'src> for Type<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - match self { - Type::Single(single) => single.get_argument_possibilities(record), - Type::Union(union) => union.get_argument_possibilities(record), - } - } -} - -/// Implemented on an AST type node to generate a snake case name. -trait GetTypeName { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String); - - fn get_type_name(&self, record: &FirstPassRecord) -> String { - let mut string = String::new(); - self.push_type_name(record, &mut string); - return string; - } -} - -impl GetTypeName for [T] { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - let mut first = true; - for union_member_type in self { - if first { - first = false; - } else { - dst.push_str("_and_"); - } - union_member_type.push_type_name(record, dst); - } - } -} - -impl GetTypeName for MayBeNull { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - if self.q_mark.is_some() { - dst.push_str("opt_"); - } - self.type_.push_type_name(record, dst); - } -} - -macro_rules! term_type_names { - ($($t:tt => $r:tt)*) => ($( - impl GetTypeName for weedle::term::$t { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str($r); - } - } - )*) -} - -term_type_names!( - Boolean => "bool" - Byte => "i8" - Octet => "u8" - Int8Array => "i8_array" - Uint8Array => "u8_array" - Uint8ClampedArray => "u8_clamped_array" - Int16Array => "i16_array" - Uint16Array => "u16_array" - Int32Array => "i32_array" - Uint32Array => "u32_array" - Float32Array => "f32_array" - Float64Array => "f64_array" - USVString => "usv_str" - ByteString => "byte_str" - DOMString => "dom_str" - ArrayBuffer => "array_buffer" - Symbol => "symbol" - Object => "object" - DataView => "data_view" - Error => "error" -); - -impl<'src> GetTypeName for weedle::types::ReturnType<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match self { - weedle::types::ReturnType::Type(ty) => (*ty).push_type_name(record, dst), - weedle::types::ReturnType::Void(_) => dst.push_str("void"), - } - } -} - -impl GetTypeName for weedle::types::StringType { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - match self { - weedle::types::StringType::Byte(_) => dst.push_str("byte_str"), - weedle::types::StringType::DOM(_) => dst.push_str("dom_str"), - weedle::types::StringType::USV(_) => dst.push_str("usv_str"), - } - } -} - -impl<'src> GetTypeName for weedle::common::Identifier<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match record.typedefs.get(self.0) { - Some(other) => other.push_type_name(record, dst), - None => dst.push_str(&self.0.to_snake_case()), - } - } -} - -impl GetTypeName for IntegerType { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - match self { - IntegerType::LongLong(l) if l.unsigned.is_some() => dst.push_str("u64"), - IntegerType::LongLong(_) => dst.push_str("i64"), - IntegerType::Long(l) if l.unsigned.is_some() => dst.push_str("u32"), - IntegerType::Long(_) => dst.push_str("i32"), - IntegerType::Short(l) if l.unsigned.is_some() => dst.push_str("u16"), - IntegerType::Short(_) => dst.push_str("i16"), - } - } -} - -impl GetTypeName for FloatingPointType { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - match self { - FloatingPointType::Float(_) => dst.push_str("f32"), - FloatingPointType::Double(_) => dst.push_str("f64"), - } - } -} - -impl<'src> GetTypeName for SequenceType<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - self.generics.body.push_type_name(record, dst); - dst.push_str("_seq"); - } -} - -impl<'src> GetTypeName for PromiseType<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - self.generics.body.push_type_name(record, dst); - dst.push_str("_promise"); - } -} - -impl<'src> GetTypeName for FrozenArrayType<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - self.generics.body.push_type_name(record, dst); - dst.push_str("_frozen_array"); - } -} - -impl<'src> GetTypeName for RecordType<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - dst.push_str("record_from_"); - self.generics.body.0.push_type_name(record, dst); - dst.push_str("_to_"); - self.generics.body.2.push_type_name(record, dst); - } -} - -impl<'a> GetTypeName for NonAnyType<'a> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match self { - NonAnyType::Boolean(s) => s.push_type_name(record, dst), - NonAnyType::Octet(s) => s.push_type_name(record, dst), - NonAnyType::Byte(s) => s.push_type_name(record, dst), - NonAnyType::Identifier(s) => s.push_type_name(record, dst), - NonAnyType::Integer(s) => s.push_type_name(record, dst), - NonAnyType::FloatingPoint(s) => s.push_type_name(record, dst), - - NonAnyType::Float32Array(s) => s.push_type_name(record, dst), - NonAnyType::Float64Array(s) => s.push_type_name(record, dst), - NonAnyType::Int8Array(s) => s.push_type_name(record, dst), - NonAnyType::Int16Array(s) => s.push_type_name(record, dst), - NonAnyType::Int32Array(s) => s.push_type_name(record, dst), - NonAnyType::Uint8Array(s) => s.push_type_name(record, dst), - NonAnyType::Uint8ClampedArray(s) => s.push_type_name(record, dst), - NonAnyType::Uint16Array(s) => s.push_type_name(record, dst), - NonAnyType::Uint32Array(s) => s.push_type_name(record, dst), - - NonAnyType::DOMString(s) => s.push_type_name(record, dst), - NonAnyType::ByteString(s) => s.push_type_name(record, dst), - NonAnyType::USVString(s) => s.push_type_name(record, dst), - NonAnyType::ArrayBuffer(s) => s.push_type_name(record, dst), - - NonAnyType::DataView(s) => s.push_type_name(record, dst), - NonAnyType::Error(s) => s.push_type_name(record, dst), - NonAnyType::FrozenArrayType(s) => s.push_type_name(record, dst), - NonAnyType::Object(s) => s.push_type_name(record, dst), - NonAnyType::Promise(s) => s.push_type_name(record, dst), - NonAnyType::RecordType(s) => s.push_type_name(record, dst), - NonAnyType::Sequence(s) => s.push_type_name(record, dst), - NonAnyType::Symbol(s) => s.push_type_name(record, dst), - } - } -} - -impl<'a> GetTypeName for SingleType<'a> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match self { - SingleType::Any(_) => dst.push_str("any"), - SingleType::NonAny(non_any) => non_any.push_type_name(record, dst), - } - } -} - -impl<'a> GetTypeName for UnionMemberType<'a> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match self { - UnionMemberType::Single(single) => single.push_type_name(record, dst), - UnionMemberType::Union(union) => union.push_type_name(record, dst), - } - } -} - -impl<'a> GetTypeName for UnionType<'a> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - dst.push_str("union_of_"); - self.body.list.push_type_name(record, dst); - } -} - -impl<'a> GetTypeName for Type<'a> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match self { - Type::Single(single) => single.push_type_name(record, dst), - Type::Union(union) => union.push_type_name(record, dst), - } - } -} - impl<'src> FirstPassRecord<'src> { - /// Uses the first pass to convert webidl function arguments to all possible argument combinations. - /// Returns option that contains a value if the conversion succeeds. - /// The value is a vector of argument possibilities. - /// Each possibility is a vector of tuples of converted argument (syn) types and type names - fn get_arguments_possibilities(&self, arguments: &'src [Argument]) -> Option>> { - fn get_argument_possibilities(record: &FirstPassRecord, argument: &Argument) -> Option> { - let type_ = match argument { - Argument::Single(single) => &single.type_.type_, - Argument::Variadic(variadic) => &variadic.type_, - }; - match type_.get_argument_possibilities(record) { - None => { - warn!("Argument's type is not yet supported: {:?}", argument); - None - }, - Some(value) => Some(value), - } - } - if !arguments.is_empty() { - let mut optional_arguments_possibilities = Vec::new(); - if let Argument::Single(ref single) = arguments[0] { - if single.optional.is_some() { - optional_arguments_possibilities.push(Vec::new()); - } - } - let mut arguments_possibilities: Vec<_> = get_argument_possibilities( - self, - &arguments[0] - )? - .into_iter() - .map(|argument_possibility| vec![argument_possibility]) - .collect(); - for argument in arguments[1..].iter() { - let mut new_arguments_possibilities = Vec::new(); - for arguments_possibility in arguments_possibilities { - if let Argument::Single(single) = argument { - if single.optional.is_some() { - optional_arguments_possibilities.push(arguments_possibility.clone()); - } - } - let mut element_argument_possibilities = get_argument_possibilities( - self, - &argument - )?; - for element_argument_possibility in element_argument_possibilities { - new_arguments_possibilities.push( - arguments_possibility - .iter() - .cloned() - .chain(iter::once(element_argument_possibility)) - .collect() - ) - } - } - arguments_possibilities = new_arguments_possibilities - } - optional_arguments_possibilities.extend(arguments_possibilities.into_iter()); - Some(optional_arguments_possibilities) - } else { - Some(vec![Vec::new()]) - } - } - - /// Uses the first pass to convert webidl function arguments to rust arguments. - /// - /// `kind` is whether the function is a method, in which case we would need a `self` - /// parameter. - /// - /// Returns option that contains a value if the conversion succeeds. - /// The value is a vector of argument variants. - /// Each variant is a vector of tuples of original arguments, converted argument types and type names. - fn get_variants( - &self, - arguments: &'src [Argument], - kind: &backend::ast::ImportFunctionKind, - ) -> Option, syn::ArgCaptured, Option)>>> - { - let arguments_possibilities = self.get_arguments_possibilities(arguments)?; - let mut result = Vec::new(); - for arguments_possibility in arguments_possibilities { - 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(arguments.len() + 1); - res.push( - ( - None, - simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone())), - None, - ), - ); - res - } else { - Vec::with_capacity(arguments.len()) - }; - for (argument, argument_possibility) in arguments.iter().zip(arguments_possibility) { - let single = match argument { - Argument::Single(single) => single, - Argument::Variadic(_) => return None, - }; - res.push( - ( - Some(argument), - simple_fn_arg( - rust_ident(&single.identifier.0.to_snake_case()), - argument_possibility.0.clone() - ), - Some(argument_possibility.1.clone()), - ), - ); - } - result.push(res); - } - - Some(result) - } - /// Create a wasm-bindgen function, if possible. pub fn create_function( &self, name: &str, overloaded: bool, same_argument_names: bool, - arguments: &[Argument], - mut ret: Option, + arguments: &[(&str, IdlType<'src>, bool)], + ret: IdlType<'src>, kind: backend::ast::ImportFunctionKind, structural: bool, catch: bool, doc_comment: Option, - ) -> Option> - { + ) -> Vec { let rust_name = if overloaded && !arguments.is_empty() { let mut argument_type_names = String::new(); for argument in arguments { - let argument = match argument { - Argument::Single(single) => single, - Argument::Variadic(_) => return None, - }; if argument_type_names.len() > 0 { argument_type_names.push_str("_and_"); } if same_argument_names { - argument.type_.type_.push_type_name(self, &mut argument_type_names); + argument.1.push_type_name(&mut argument_type_names); } else { - argument_type_names.push_str(&argument.identifier.0.to_snake_case()); + argument_type_names.push_str(&argument.0.to_snake_case()); } } if name == "new" { @@ -1006,46 +230,53 @@ impl<'src> FirstPassRecord<'src> { name.to_snake_case() }; + let ret = match ret { + IdlType::Void => None, + ret @ _ => { + match ret.to_syn_type(TypePosition::Return) { + None => return Vec::new(), + Some(ret) => Some(ret), + } + }, + }; + let js_ret = ret.clone(); - if catch { - ret = Some(ret.map_or_else(|| result_ty(unit_ty()), result_ty)) - } + let ret = if catch { + Some(ret.map_or_else(|| result_ty(unit_ty()), result_ty)) + } else { + ret + }; - let variants = self.get_variants(arguments, &kind)?; - let mut arguments_count_variants_count = BTreeMap::new(); - for variant in &variants { - arguments_count_variants_count - .entry(variant.len()) - .and_modify(|variants_count| { *variants_count += 1usize; }) - .or_insert(1usize); + let converted_arguments = arguments + .iter() + .cloned() + .map(|(_name, idl_type, optional)| (idl_type, optional)) + .collect::>(); + let possibilities = flatten(&converted_arguments); + let mut arguments_count_multiple = BTreeMap::new(); + for idl_types in &possibilities { + arguments_count_multiple + .entry(idl_types.len()) + .and_modify(|variants_count| { *variants_count = true; }) + .or_insert(false); } - let mut result = Vec::new(); - for variant in &variants { - let rust_name = if variants.len() > 1 { + let mut import_functions = Vec::new(); + 'outer: for idl_types in &possibilities { + let rust_name = if possibilities.len() > 1 { let mut rust_name = rust_name.clone(); let mut first = true; - for (argument, _, variant_name) in variant { - if let Some(type_name) = variant_name { - if first { - rust_name.push_str("_using_"); - first = false; - } else { - rust_name.push_str("_and_"); - } - if arguments_count_variants_count[&variant.len()] == 1 { - if let Some(argument) = argument { - let argument = match argument { - Argument::Single(single) => single, - Argument::Variadic(_) => return None, - }; - rust_name.push_str(&argument.identifier.0.to_snake_case()); - } else { - rust_name.push_str(&type_name); - } - } else { - rust_name.push_str(&type_name); - } + for ((argument_name, _, _), idl_type) in arguments.iter().zip(idl_types) { + if first { + rust_name.push_str("_using_"); + first = false; + } else { + rust_name.push_str("_and_"); + } + if arguments_count_multiple[&idl_types.len()] { + idl_type.push_type_name(&mut rust_name); + } else { + rust_name.push_str(&argument_name.to_snake_case()); } } rust_name @@ -1062,10 +293,35 @@ impl<'src> FirstPassRecord<'src> { raw_ident(&format!("__widl_f_{}_{}", rust_name, ns)) }; - result.push(backend::ast::ImportFunction { + let mut args_captured = if let &backend::ast::ImportFunctionKind::Method { + ref ty, + kind: backend::ast::MethodKind::Operation( + backend::ast::Operation { + is_static: false, .. + } + ), + .. + } = &kind { + let mut res = Vec::with_capacity(idl_types.len() + 1); + res.push(simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone()))); + res + } else { + Vec::with_capacity(idl_types.len()) + }; + for ((argument_name, _, _), idl_type) in arguments.iter().zip(idl_types) { + let syn_type = if let Some(syn_type) = idl_type.to_syn_type(TypePosition::Argument) { + syn_type + } else { + continue 'outer; + }; + let argument_name = rust_ident(&argument_name.to_snake_case()); + args_captured.push(simple_fn_arg(argument_name, syn_type)); + } + + import_functions.push(backend::ast::ImportFunction { function: backend::ast::Function { name: name.to_string(), - arguments: variant.iter().map(|variant| variant.1.clone()).collect(), + arguments: args_captured, ret: ret.clone(), rust_attrs: vec![], rust_vis: public(), @@ -1079,7 +335,32 @@ impl<'src> FirstPassRecord<'src> { doc_comment: doc_comment.clone(), }) } - Some(result) + import_functions + } + + /// Convert arguments to ones suitable crating function + pub(crate) fn convert_arguments( + &self, + arguments: &[weedle::argument::Argument<'src>], + ) -> Option, bool)>> { + let mut converted_arguments = Vec::with_capacity(arguments.len()); + for argument in arguments { + let name = match argument { + Argument::Single(single) => single.identifier.0, + Argument::Variadic(variadic) => variadic.identifier.0, + }; + let ty = match argument { + Argument::Single(single) => &single.type_.type_, + Argument::Variadic(variadic) => &variadic.type_, + }; + let idl_type = ty.to_idl_type(self)?; + let optional = match argument { + Argument::Single(single) => single.optional.is_some(), + Argument::Variadic(_variadic) => false, + }; + converted_arguments.push((name, idl_type, optional)); + } + Some(converted_arguments) } /// Create a wasm-bindgen method, if possible. @@ -1092,7 +373,7 @@ impl<'src> FirstPassRecord<'src> { is_static: bool, structural: bool, catch: bool, - ) -> Option> { + ) -> Vec { let (overloaded, same_argument_names) = self.get_operation_overloading( arguments, &operation_id, @@ -1104,7 +385,7 @@ impl<'src> FirstPassRecord<'src> { ::first_pass::OperationId::Operation(name) => match name { None => { warn!("Operations without a name are unsupported"); - return None; + return Vec::new(); } Some(name) => name.to_string(), }, @@ -1128,18 +409,11 @@ impl<'src> FirstPassRecord<'src> { }), }; - let ret = match return_type { - weedle::types::ReturnType::Void(_) => None, - weedle::types::ReturnType::Type(ty) => { - match ty.to_syn_type(self, TypePosition::Return) { - None => { - warn!("Operation's return type is not yet supported: {:?}", ty); - return None; - } - Some(ty) => Some(ty), - } - } + let ret = match return_type.to_idl_type(self) { + None => return Vec::new(), + Some(idl_type) => idl_type, }; + let doc_comment = match &operation_id { ::first_pass::OperationId::Constructor => panic!("constructors are unsupported"), ::first_pass::OperationId::Operation(_) => Some( @@ -1154,11 +428,16 @@ impl<'src> FirstPassRecord<'src> { ::first_pass::OperationId::IndexingDeleter => Some("The indexing deleter\n\n".to_string()), }; + let arguments = match self.convert_arguments(arguments) { + None => return Vec::new(), + Some(arguments) => arguments + }; + self.create_function( &name, overloaded, same_argument_names, - arguments, + &arguments, ret, kind, structural, @@ -1230,13 +509,10 @@ impl<'src> FirstPassRecord<'src> { is_static: bool, is_structural: bool, catch: bool, - ) -> Option> { - let ret = match ty.to_syn_type(self, TypePosition::Return) { - None => { - warn!("Attribute's type does not yet support reading: {:?}", ty); - return None; - } - Some(ty) => Some(ty), + ) -> Vec { + let ret = match ty.to_idl_type(self) { + None => return Vec::new(), + Some(idl_type) => idl_type, }; let kind = backend::ast::ImportFunctionKind::Method { @@ -1261,7 +537,7 @@ impl<'src> FirstPassRecord<'src> { is_static: bool, is_structural: bool, catch: bool, - ) -> Option> { + ) -> Vec { let kind = backend::ast::ImportFunctionKind::Method { class: self_name.to_string(), ty: ident_ty(rust_ident(camel_case_ident(&self_name).as_str())), @@ -1276,17 +552,15 @@ impl<'src> FirstPassRecord<'src> { &format!("set_{}", name), false, false, - &[Argument::Single(SingleArgument { - attributes: None, - optional: None, - type_: AttributedType { - attributes: None, - type_: ty, + &[( + name, + match ty.to_idl_type(self) { + None => return Vec::new(), + Some(idl_type) => idl_type, }, - identifier: Identifier(name), - default: None, - })], - None, + false, + )], + IdlType::Void, kind, is_structural, catch, From 7840367476bb14fafa1466fa24e127682cf41ced Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Mon, 13 Aug 2018 19:21:05 +0300 Subject: [PATCH 13/14] Fix console test --- crates/web-sys/tests/wasm/console.rs | 5 ++--- crates/webidl-tests/namespace.webidl | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/web-sys/tests/wasm/console.rs b/crates/web-sys/tests/wasm/console.rs index 61ce5c29..c2289293 100644 --- a/crates/web-sys/tests/wasm/console.rs +++ b/crates/web-sys/tests/wasm/console.rs @@ -1,9 +1,8 @@ use wasm_bindgen_test::*; -use wasm_bindgen::prelude::*; use web_sys::console; #[wasm_bindgen_test] fn test_console() { - console::time("test label"); - console::time_end("test label"); + console::time_using_label("test label"); + console::time_end_using_label("test label"); } diff --git a/crates/webidl-tests/namespace.webidl b/crates/webidl-tests/namespace.webidl index 65b20dcb..598c508a 100644 --- a/crates/webidl-tests/namespace.webidl +++ b/crates/webidl-tests/namespace.webidl @@ -1,4 +1,4 @@ namespace math_test { - long add_one(long val); - double pow(double base, double exponent); + long add_one(long val); + double pow(double base, double exponent); }; From c666f752fa80632ce6a73c73a478d15fa5e72077 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Mon, 13 Aug 2018 23:18:16 +0300 Subject: [PATCH 14/14] Add OptionalAndUnionArguments test --- crates/webidl-tests/simple.js | 9 ++++++++- crates/webidl-tests/simple.rs | 17 +++++++++++++++-- crates/webidl-tests/simple.webidl | 12 +++++++++++- crates/webidl/src/lib.rs | 5 ++++- crates/webidl/src/util.rs | 6 +++++- 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/crates/webidl-tests/simple.js b/crates/webidl-tests/simple.js index 7dbd45b4..0c63082b 100644 --- a/crates/webidl-tests/simple.js +++ b/crates/webidl-tests/simple.js @@ -67,7 +67,7 @@ global.UndefinedMethod = class UndefinedMethod { } }; -global.OptionalMethod = class OptionalMethod { +global.NullableMethod = class NullableMethod { constructor() {} opt(a) { if (a == undefined) { @@ -107,6 +107,13 @@ global.Indexing = function () { }); }; +global.OptionalAndUnionArguments = class OptionalAndUnionArguments { + constructor() {} + m(a, b = true, c = 123, d = 456) { + return [typeof a, a, typeof b, b, typeof c, c, typeof d, d].join(', '); + } +}; + global.PartialInterface = class PartialInterface { get un() { return 1; diff --git a/crates/webidl-tests/simple.rs b/crates/webidl-tests/simple.rs index 1230bfbc..b06d9dd4 100644 --- a/crates/webidl-tests/simple.rs +++ b/crates/webidl-tests/simple.rs @@ -56,8 +56,8 @@ fn one_method_using_an_undefined_import_doesnt_break_all_other_methods() { } #[wasm_bindgen_test] -fn optional_method() { - let f = OptionalMethod::new().unwrap(); +fn nullable_method() { + let f = NullableMethod::new().unwrap(); assert!(f.opt(Some(15)) == Some(16)); assert!(f.opt(None) == None); } @@ -78,6 +78,19 @@ fn indexing() { assert_eq!(f.get(123), -1); } +#[wasm_bindgen_test] +fn optional_and_union_arguments() { + let f = OptionalAndUnionArguments::new().unwrap(); + assert_eq!(f.m_using_a("abc"), "string, abc, boolean, true, number, 123, number, 456"); + assert_eq!(f.m_using_a_and_b("abc", false), "string, abc, boolean, false, number, 123, number, 456"); + assert_eq!(f.m_using_dom_str_and_bool_and_i16("abc", false, 5), "string, abc, boolean, false, number, 5, number, 456"); + assert_eq!(f.m_using_dom_str_and_bool_and_dom_str("abc", false, "5"), "string, abc, boolean, false, string, 5, number, 456"); + assert_eq!(f.m_using_dom_str_and_bool_and_i16_and_opt_i64("abc", false, 5, Some(10)), "string, abc, boolean, false, number, 5, bigint, 10"); + assert_eq!(f.m_using_dom_str_and_bool_and_i16_and_opt_bool("abc", false, 5, Some(true)), "string, abc, boolean, false, number, 5, boolean, true"); + assert_eq!(f.m_using_dom_str_and_bool_and_dom_str_and_opt_i64("abc", false, "5", Some(10)), "string, abc, boolean, false, string, 5, bigint, 10"); + assert_eq!(f.m_using_dom_str_and_bool_and_dom_str_and_opt_bool("abc", false, "5", Some(true)), "string, abc, boolean, false, string, 5, boolean, true"); +} + #[wasm_bindgen_test] fn unforgeable_is_structural() { let f = Unforgeable::new().unwrap(); diff --git a/crates/webidl-tests/simple.webidl b/crates/webidl-tests/simple.webidl index 8435c024..6df6de63 100644 --- a/crates/webidl-tests/simple.webidl +++ b/crates/webidl-tests/simple.webidl @@ -31,7 +31,7 @@ interface UndefinedMethod { }; [Constructor()] -interface OptionalMethod { +interface NullableMethod { octet? opt(short? a); }; @@ -47,6 +47,16 @@ interface Indexing { deleter void (unsigned long index); }; +[Constructor()] +interface OptionalAndUnionArguments { + DOMString m( + DOMString a, + optional boolean b = true, + optional (short or DOMString) c = 123, + optional (long long or boolean)? d = 456 + ); +}; + [Constructor()] interface Unforgeable { [Unforgeable] readonly attribute short uno; diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 7cbae081..23163f9f 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -789,7 +789,10 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> }; let ty = match idl_type.to_syn_type(TypePosition::Return) { - None => return Ok(()), + None => { + warn!("Can not convert const type to syn type: {:?}", idl_type); + return Ok(()); + }, Some(ty) => ty, }; diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index eecd403b..0722d2c8 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -234,7 +234,10 @@ impl<'src> FirstPassRecord<'src> { IdlType::Void => None, ret @ _ => { match ret.to_syn_type(TypePosition::Return) { - None => return Vec::new(), + None => { + warn!("Can not convert return type to syn type: {:?}", ret); + return Vec::new(); + }, Some(ret) => Some(ret), } }, @@ -312,6 +315,7 @@ impl<'src> FirstPassRecord<'src> { let syn_type = if let Some(syn_type) = idl_type.to_syn_type(TypePosition::Argument) { syn_type } else { + warn!("Can not convert argument type to syn type: {:?}", idl_type); continue 'outer; }; let argument_name = rust_ident(&argument_name.to_snake_case());