diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index d8eb5761..7e694230 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeMap; use proc_macro2::{Ident, Span}; use shared; use syn; @@ -8,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, @@ -21,12 +20,13 @@ pub struct Program { /// rust consts pub consts: Vec, /// rust submodules - pub modules: BTreeMap, + pub modules: Vec, } /// 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, @@ -47,6 +47,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, @@ -57,6 +58,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, @@ -64,6 +66,7 @@ pub struct Import { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub enum ImportKind { Function(ImportFunction), Static(ImportStatic), @@ -72,6 +75,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, @@ -84,6 +88,7 @@ pub struct ImportFunction { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub enum ImportFunctionKind { Method { class: String, @@ -94,18 +99,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), @@ -116,6 +124,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, @@ -125,6 +134,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, @@ -136,6 +146,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, @@ -150,6 +161,7 @@ pub struct ImportEnum { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Function { pub name: String, pub arguments: Vec, @@ -159,6 +171,7 @@ pub struct Function { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Struct { pub name: Ident, pub fields: Vec, @@ -166,6 +179,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, @@ -177,6 +191,7 @@ pub struct StructField { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Enum { pub name: Ident, pub variants: Vec, @@ -184,6 +199,7 @@ pub struct Enum { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Variant { pub name: Ident, pub value: u32, @@ -205,6 +221,7 @@ pub enum TypeLocation { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))] +#[derive(Clone)] pub struct Const { pub vis: syn::Visibility, pub name: Ident, @@ -214,6 +231,7 @@ pub struct Const { } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))] +#[derive(Clone)] /// same as webidl::ast::ConstValue pub enum ConstValue { BooleanLiteral(bool), @@ -227,8 +245,10 @@ pub enum ConstValue { /// /// This exists to give the ability to namespace js imports. #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Clone)] pub struct Module { pub vis: syn::Visibility, + pub name: Ident, /// js -> rust interfaces pub imports: Vec, } @@ -241,7 +261,7 @@ impl Program { enums: self.enums.iter().map(|a| a.shared()).collect(), imports: self.imports.iter() // add in imports from inside modules - .chain(self.modules.values().flat_map(|m| m.imports.iter())) + .chain(self.modules.iter().flat_map(|m| m.imports.iter())) .map(|a| a.shared()) .collect::>()?, version: shared::version(), diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index ac8f1096..95e1ef9c 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -63,17 +63,17 @@ impl TryToTokens for ast::Program { errors.push(e); } } - for m in self.modules.iter() { - if let Err(e) = ModuleInIter::from(m).try_to_tokens(tokens) { - errors.push(e); - } - } for e in self.enums.iter() { e.to_tokens(tokens); } for c in self.consts.iter() { c.to_tokens(tokens); } + for m in self.modules.iter() { + if let Err(e) = m.try_to_tokens(tokens) { + errors.push(e); + } + } Diagnostic::from_vec(errors)?; @@ -1111,30 +1111,17 @@ impl ToTokens for ast::Const { } } -/// Struct to help implementing TryToTokens over the key/value pairs from the hashmap. -struct ModuleInIter<'a> { - name: &'a Ident, - module: &'a ast::Module -} - -impl<'a> From<(&'a Ident, &'a ast::Module)> for ModuleInIter<'a> { - fn from((name, module): (&'a Ident, &'a ast::Module)) -> ModuleInIter<'a> { - ModuleInIter { name, module } - } -} - -impl<'a> TryToTokens for ModuleInIter<'a> { +impl<'a> TryToTokens for ast::Module { fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { - let name = &self.name; - let imports = &self.module.imports; - let mut errors = Vec::new(); - for i in imports.iter() { - DescribeImport(&i.kind).to_tokens(tokens); + for import in &self.imports { + DescribeImport(&import.kind).to_tokens(tokens); } - let vis = &self.module.vis; + let vis = &self.vis; + let name = &self.name; + let mut errors = Vec::new(); let mut body = TokenStream::new(); - for i in imports.iter() { - if let Err(e) = i.kind.try_to_tokens(&mut body) { + for import in &self.imports { + if let Err(e) = import.kind.try_to_tokens(&mut body) { errors.push(e); } } 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/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, diff --git a/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl b/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl index 336687ff..95ba590a 100644 --- a/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl +++ b/crates/web-sys/webidls/enabled/WebGL2RenderingContext.webidl @@ -26,13 +26,13 @@ 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 { }; -[NoInterfaceObject] -interface WebGL2RenderingContextBase +interface mixin WebGL2RenderingContextBase { const GLenum READ_BUFFER = 0x0C02; const GLenum UNPACK_ROW_LENGTH = 0x0CF2; @@ -694,8 +694,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-tests/main.rs b/crates/webidl-tests/main.rs index 12635f50..0bcae6b8 100644 --- a/crates/webidl-tests/main.rs +++ b/crates/webidl-tests/main.rs @@ -8,6 +8,6 @@ pub mod array; pub mod array_buffer; pub mod consts; pub mod enums; +pub mod namespace; pub mod simple; pub mod throws; -pub mod namespace; diff --git a/crates/webidl-tests/namespace.js b/crates/webidl-tests/namespace.js index 7c86dcd5..c9e362dc 100644 --- a/crates/webidl-tests/namespace.js +++ b/crates/webidl-tests/namespace.js @@ -1,11 +1,9 @@ -const strictEqual = require('assert').strictEqual; +global.math_test = { + pow(base, exp) { + return Math.pow(base, exp); + }, -global.mathtest = {}; - -global.mathtest.powf = function powf(base, exp) { - return Math.pow(base, exp); -} - -global.mathtest.add_one = function add_one(val) { - return val + 1; -} + add_one(val) { + return val + 1; + }, +}; diff --git a/crates/webidl-tests/namespace.rs b/crates/webidl-tests/namespace.rs index 49afc623..ec2fde2e 100644 --- a/crates/webidl-tests/namespace.rs +++ b/crates/webidl-tests/namespace.rs @@ -4,7 +4,7 @@ include!(concat!(env!("OUT_DIR"), "/namespace.rs")); #[wasm_bindgen_test] fn simple_namespace_test() { - assert_eq!(mathtest::add_one(1), 2); - assert_eq!(mathtest::powf(1.0, 100.0), 1.0); - assert_eq!(mathtest::powf(10.0, 2.0), 100.0); + assert_eq!(math_test::add_one(1), 2); + assert_eq!(math_test::pow(1.0, 100.0), 1.0); + assert_eq!(math_test::pow(10.0, 2.0), 100.0); } diff --git a/crates/webidl-tests/namespace.webidl b/crates/webidl-tests/namespace.webidl index 1c3dbf3a..598c508a 100644 --- a/crates/webidl-tests/namespace.webidl +++ b/crates/webidl-tests/namespace.webidl @@ -1,4 +1,4 @@ -namespace mathtest { - long add_one(long val); - double powf(double base, double exponent); +namespace math_test { + long add_one(long val); + double pow(double base, double exponent); }; 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/first_pass.rs b/crates/webidl/src/first_pass.rs index f6c109fe..f3637f61 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -12,7 +12,8 @@ use std::collections::{BTreeMap, BTreeSet}; use weedle::argument::Argument; use weedle::attribute::ExtendedAttribute; use weedle::interface::StringifierOrStatic; -use weedle::mixin::MixinMembers; +use weedle::mixin::MixinMember; +use weedle::namespace::NamespaceMember; use weedle; use super::Result; @@ -26,9 +27,10 @@ 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) namespaces: BTreeMap<&'src str, NamespaceData<'src>>, + pub(crate) includes: BTreeMap<&'src str, BTreeSet<&'src str>>, } /// We need to collect interface data during the first pass, to be used later. @@ -41,31 +43,25 @@ pub(crate) struct InterfaceData<'src> { pub(crate) superclass: Option<&'src str>, } +/// 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 MixinMember<'src>>, + pub(crate) operations: BTreeMap, OperationData<'src>>, +} + /// We need to collect namespace data during the first pass, to be used later. +#[derive(Default)] pub(crate) struct NamespaceData<'src> { /// Whether only partial namespaces were encountered pub(crate) partial: bool, - pub(crate) operations: BTreeMap, OperationData<'src>>, + pub(crate) members: Vec<&'src NamespaceMember<'src>>, + pub(crate) operations: BTreeMap, OperationData<'src>>, } -impl<'src> NamespaceData<'src> { - /// Creates an empty node for a non-partial namespace. - pub(crate) fn empty_non_partial() -> Self { - Self { - partial: false, - operations: Default::default(), - } - } - /// Creates an empty node for a partial namespace. - pub(crate) fn empty_partial() -> Self { - Self { - partial: true, - operations: Default::default(), - } - } -} - -#[derive(PartialEq, Eq, PartialOrd, Ord)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] pub(crate) enum OperationId<'src> { Constructor, Operation(Option<&'src str>), @@ -104,6 +100,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, ()), @@ -121,15 +118,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); } @@ -138,25 +144,66 @@ impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> { } } -/// Helper function to add an operation to an interface. -fn first_pass_interface_operation<'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(()) + } +} + +#[derive(Clone, Copy)] +enum FirstPassOperationType { + Interface, + Mixin, + Namespace, +} + +fn first_pass_operation<'src>( record: &mut FirstPassRecord<'src>, + first_pass_operation_type: FirstPassOperationType, 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 + match first_pass_operation_type{ + FirstPassOperationType::Interface => { + &mut record + .interfaces + .get_mut(self_name) + .expect(&format!("not found {} interface", self_name)) + .operations + }, + FirstPassOperationType::Mixin => { + &mut record + .mixins + .get_mut(self_name) + .expect(&format!("not found {} mixin", self_name)) + .operations + }, + FirstPassOperationType::Namespace => { + &mut record + .namespaces + .get_mut(self_name) + .expect(&format!("not found {} namesace", self_name)) + .operations + }, + } .entry(id) .and_modify(|operation_data| operation_data.overloaded = true) .or_default() @@ -170,17 +217,17 @@ fn first_pass_interface_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 = record + let interface_data = record .interfaces .entry(self.identifier.0) .or_default(); - interface.partial = false; - interface.superclass = self.inheritance.map(|s| s.identifier.0); - } - - if util::is_chrome_only(&self.attributes) { - return Ok(()) + interface_data.partial = false; + interface_data.superclass = self.inheritance.map(|s| s.identifier.0); } if let Some(attrs) = &self.attributes { @@ -199,22 +246,22 @@ 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) .or_insert_with(|| InterfaceData { partial: true, - operations: Default::default(), global: false, + operations: Default::default(), superclass: None, }, ); - if util::is_chrome_only(&self.attributes) { - return Ok(()) - } - for member in &self.members.body { member.first_pass(record, self.identifier.0)?; } @@ -227,16 +274,18 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { match self { ExtendedAttribute::ArgList(list) if list.identifier.0 == "Constructor" => { - first_pass_interface_operation( + first_pass_operation( record, + FirstPassOperationType::Interface, self_name, OperationId::Constructor, &list.args.body.list, ) } ExtendedAttribute::NoArgs(name) if (name.0).0 == "Constructor" => { - first_pass_interface_operation( + first_pass_operation( record, + FirstPassOperationType::Interface, self_name, OperationId::Constructor, &[], @@ -245,8 +294,9 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { ExtendedAttribute::NamedArgList(list) if list.lhs_identifier.0 == "NamedConstructor" => { - first_pass_interface_operation( + first_pass_operation( record, + FirstPassOperationType::Interface, self_name, OperationId::Constructor, &list.args.body.list, @@ -278,6 +328,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(()) @@ -286,20 +340,17 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM warn!("Unsupported webidl operation {:?}", self); return Ok(()) } - first_pass_interface_operation( + first_pass_operation( record, + FirstPassOperationType::Interface, self_name, match self.identifier.map(|s| s.0) { None => match self.specials.get(0) { None => OperationId::Operation(None), - Some(weedle::interface::Special::Getter(weedle::term::Getter)) - => OperationId::IndexingGetter, - Some(weedle::interface::Special::Setter(weedle::term::Setter)) - => OperationId::IndexingSetter, - Some(weedle::interface::Special::Deleter(weedle::term::Deleter)) - => OperationId::IndexingDeleter, - Some(weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller)) - => return Ok(()), + Some(weedle::interface::Special::Getter(_)) => OperationId::IndexingGetter, + Some(weedle::interface::Special::Setter(_)) => OperationId::IndexingSetter, + Some(weedle::interface::Special::Deleter(_)) => OperationId::IndexingDeleter, + Some(weedle::interface::Special::LegacyCaller(_)) => return Ok(()), }, Some(ref name) => OperationId::Operation(Some(name.clone())), }, @@ -310,26 +361,85 @@ 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); + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + + { + let mixin_data = record + .mixins + .entry(self.identifier.0) + .or_insert_with(Default::default); + mixin_data.partial = false; + mixin_data.members.extend(&self.members.body); + } + + for member in &self.members.body { + member.first_pass(record, self.identifier.0)?; + } + Ok(()) } } 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) - .or_insert_with(Default::default) - .push(&self.members.body); + .or_insert_with(|| + MixinData { + partial: true, + members: Default::default(), + operations: Default::default(), + }, + ) + .members + .extend(&self.members.body); + + for member in &self.members.body { + member.first_pass(record, self.identifier.0)?; + } + 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 util::is_chrome_only(&self.attributes) { + return Ok(()); + } + + if self.stringifier.is_some() { + warn!("Unsupported webidl operation {:?}", self); + return Ok(()) + } + first_pass_operation( + record, + FirstPassOperationType::Mixin, + 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) { @@ -346,17 +456,23 @@ impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> { impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { - record - .namespaces - .entry(self.identifier.0) - .and_modify(|entry| entry.partial = false) - .or_insert_with(NamespaceData::empty_non_partial); - if util::is_chrome_only(&self.attributes) { return Ok(()) } - // We ignore all attributes. + record + .namespaces + .entry(self.identifier.0) + .and_modify(|namespace_data| namespace_data.partial = false) + .or_insert_with(|| + NamespaceData { + partial: true, + members: Default::default(), + operations: Default::default(), + }, + ) + .members + .extend(&self.members.body); for member in &self.members.body { member.first_pass(record, self.identifier.0)?; @@ -368,15 +484,17 @@ impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> { impl<'src> FirstPass<'src, ()> for weedle::PartialNamespaceDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { - record - .namespaces - .entry(self.identifier.0) - .or_insert_with(NamespaceData::empty_partial); - if util::is_chrome_only(&self.attributes) { return Ok(()) } + record + .namespaces + .entry(self.identifier.0) + .or_default() + .members + .extend(&self.members.body); + for member in &self.members.body { member.first_pass(record, self.identifier.0)?; } @@ -386,13 +504,10 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialNamespaceDefinition<'src> { } impl<'src> FirstPass<'src, &'src str> for weedle::namespace::NamespaceMember<'src> { - fn first_pass(&'src self, - record: &mut FirstPassRecord<'src>, - namespace_name: &'src str) -> Result<()> - { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { match self { weedle::namespace::NamespaceMember::Operation(op) => { - op.first_pass(record, namespace_name) + op.first_pass(record, self_name) } _ => Ok(()), } @@ -400,32 +515,18 @@ impl<'src> FirstPass<'src, &'src str> for weedle::namespace::NamespaceMember<'sr } impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> { - fn first_pass(&'src self, - record: &mut FirstPassRecord<'src>, - namespace_name: &'src str) -> Result<()> - { - let identifier = self.identifier.map(|s| s.0); - let arguments = &self.args.body.list; - let mut names = Vec::with_capacity(arguments.len()); - for argument in arguments { - match argument { - Argument::Single(arg) => names.push(arg.identifier.0), - Argument::Variadic(_) => return Ok(()), - } + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()) } - record - .namespaces - .get_mut(namespace_name) - .unwrap() // call this after creating the namespace - .operations - .entry(identifier) - .and_modify(|operation_data| operation_data.overloaded = true) - .or_insert_with(Default::default) - .argument_names_same - .entry(names) - .and_modify(|same_argument_names| *same_argument_names = true) - .or_insert(false); - Ok(()) + + first_pass_operation( + record, + FirstPassOperationType::Namespace, + self_name, + OperationId::Operation(self.identifier.map(|s| s.0.clone())), + &self.args.body.list, + ) } } 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 4d139c0c..23163f9f 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}; @@ -168,18 +169,17 @@ 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, ())? } weedle::Definition::PartialInterface(interface) => { interface.webidl_parse(program, first_pass, ())? } - weedle::Definition::Typedef(_) | - weedle::Definition::InterfaceMixin(_) | - weedle::Definition::PartialInterfaceMixin(_) => { + | weedle::Definition::Typedef(_) + | weedle::Definition::InterfaceMixin(_) + | weedle::Definition::PartialInterfaceMixin(_) + | weedle::Definition::IncludesStatement(..) + | weedle::Definition::PartialNamespace(..)=> { // handled in the first pass } weedle::Definition::Implements(..) => { @@ -188,11 +188,8 @@ impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> { weedle::Definition::Namespace(namespace) => { namespace.webidl_parse(program, first_pass, ())? } - weedle::Definition::PartialNamespace(namespace) => { - namespace.webidl_parse(program, first_pass, ())? - } // TODO - weedle::Definition::Callback(..) + | weedle::Definition::Callback(..) | weedle::Definition::CallbackInterface(..) | weedle::Definition::Dictionary(..) | weedle::Definition::PartialDictionary(..) => { @@ -203,25 +200,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, @@ -265,10 +243,31 @@ 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 member in &mixin_data.members { + member.webidl_parse(program, first_pass, self_name)?; + } + } + if let Some(mixin_names) = first_pass.includes.get(mixin_name) { + for mixin_name in mixin_names { + parse(program, first_pass, self_name, mixin_name)?; + } + } + Ok(()) + } + for member in &self.members.body { member.webidl_parse(program, first_pass, self.identifier.0)?; } + parse(program, first_pass, self.identifier.0, self.identifier.0)?; + Ok(()) } } @@ -284,7 +283,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 @@ -307,10 +310,11 @@ impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for Extend interface: &'src weedle::InterfaceDefinition<'src>, ) -> Result<()> { let mut add_constructor = |arguments: &[Argument], class: &str| { - let (overloaded, same_argument_names) = first_pass.get_method_overloading( + let (overloaded, same_argument_names) = first_pass.get_operation_overloading( arguments, &::first_pass::OperationId::Constructor, interface.identifier.0, + false, ); let self_ty = ident_ty(rust_ident(camel_case_ident(interface.identifier.0).as_str())); @@ -337,20 +341,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(wrap_import_function) - .map(|import| program.imports.push(import)); + 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 { @@ -440,8 +446,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) @@ -541,7 +547,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(()) @@ -559,30 +565,28 @@ fn member_attribute<'src>( let is_structural = util::is_structural(attrs); let throws = util::throws(attrs); - first_pass - .create_getter( - identifier, - &type_.type_, - self_name, - statik, - is_structural, - throws, - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); + for import_function in first_pass.create_getter( + identifier, + &type_.type_, + self_name, + is_static, + is_structural, + throws, + ) { + program.imports.push(wrap_import_function(import_function)); + } if !readonly { - first_pass - .create_setter( - identifier, - type_.type_.clone(), - self_name, - statik, - is_structural, - throws, - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); + 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(()) @@ -646,7 +650,8 @@ 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(()) @@ -655,40 +660,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(_) => ::first_pass::OperationId::IndexingGetter, + weedle::interface::Special::Setter(_) => ::first_pass::OperationId::IndexingSetter, + weedle::interface::Special::Deleter(_) => ::first_pass::OperationId::IndexingDeleter, + weedle::interface::Special::LegacyCaller(_) => return Ok(()), }, - return_type, - self_name, - statik, - specials.len() == 1 || first_pass - .interfaces - .get(self_name) - .map(|interface_data| interface_data.global) - .unwrap_or(false), - util::throws(attrs), - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); + 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(()) } @@ -743,6 +742,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, @@ -776,9 +779,21 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> first_pass: &FirstPassRecord<'src>, self_name: &'src str, ) -> Result<()> { - let ty = match self.const_type.to_syn_type(first_pass, TypePosition::Return) { - Some(s) => s, + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + + let idl_type = match self.const_type.to_idl_type(first_pass) { None => return Ok(()), + Some(idl_type) => idl_type, + }; + + let ty = match idl_type.to_syn_type(TypePosition::Return) { + None => { + warn!("Can not convert const type to syn type: {:?}", idl_type); + return Ok(()); + }, + Some(ty) => ty, }; program.consts.push(backend::ast::Const { @@ -804,60 +819,25 @@ impl<'src> WebidlParse<'src, ()> for weedle::NamespaceDefinition<'src> { return Ok(()); } - let rust_name = rust_ident(self.identifier.0.to_snake_case().as_str()); - - program.modules.entry(rust_name.clone()) - .and_modify(|_| warn!("Namespace with rust name {:?} added more than once", rust_name)) - .or_insert_with(|| backend::ast::Module { - vis: public(), - imports: Default::default() - }); - if let Some(attrs) = &self.attributes { for attr in &attrs.body.list { attr.webidl_parse(program, first_pass, self)?; } } - let namespace_names = NamespaceNames { - rust_name: &rust_name, - js_name: &self.identifier.0, + let mut module = backend::ast::Module { + vis: public(), + name: rust_ident(self.identifier.0.to_snake_case().as_str()), + imports: Default::default(), }; - for member in &self.members.body { - member.webidl_parse(program, first_pass, namespace_names)?; + + if let Some(namespace_data) = first_pass.namespaces.get(&self.identifier.0) { + for member in &namespace_data.members { + member.webidl_parse(program, first_pass, (&self.identifier.0, &mut module))?; + } } - Ok(()) - } -} - -impl<'src> WebidlParse<'src, ()> for weedle::PartialNamespaceDefinition<'src> { - fn webidl_parse( - &'src self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'src>, - (): (), - ) -> Result<()> { - if util::is_chrome_only(&self.attributes) { - return Ok(()); - } - - let rust_name = rust_ident(self.identifier.0.to_snake_case().as_str()); - - if !first_pass.namespaces.contains_key(self.identifier.0) { - warn!( - "Partial namespace {} missing non-partial namespace", - self.identifier.0 - ); - } - - let namespace_names = NamespaceNames { - rust_name: &rust_name, - js_name: &self.identifier.0, - }; - for member in &self.members.body { - member.webidl_parse(program, first_pass, namespace_names)?; - } + program.modules.push(module); Ok(()) } @@ -874,22 +854,17 @@ impl<'src> WebidlParse<'src, &'src weedle::NamespaceDefinition<'src>> for Extend Ok(()) } } -#[derive(Copy, Clone)] -struct NamespaceNames<'a> { - rust_name: &'a Ident, - js_name: &'a str, -} -impl<'src> WebidlParse<'src, NamespaceNames<'src>> for weedle::namespace::NamespaceMember<'src> { +impl<'src> WebidlParse<'src, (&'src str, &'src mut backend::ast::Module)> for weedle::namespace::NamespaceMember<'src> { fn webidl_parse( &'src self, program: &mut backend::ast::Program, first_pass: &FirstPassRecord<'src>, - ns_names: NamespaceNames<'src> + (self_name, module): (&'src str, &mut backend::ast::Module), ) -> Result<()> { match self { weedle::namespace::NamespaceMember::Operation(op) => { - op.webidl_parse(program, first_pass, ns_names)?; + op.webidl_parse(program, first_pass, (self_name, module))?; } weedle::namespace::NamespaceMember::Attribute(_) => { warn!("Attribute namespace members are not supported") @@ -899,42 +874,33 @@ impl<'src> WebidlParse<'src, NamespaceNames<'src>> for weedle::namespace::Namesp } } -impl<'src> WebidlParse<'src, NamespaceNames<'src>> for weedle::namespace::OperationNamespaceMember<'src> { +impl<'src> WebidlParse<'src, (&'src str, &'src mut backend::ast::Module)> for weedle::namespace::OperationNamespaceMember<'src> { fn webidl_parse( &'src self, - program: &mut backend::ast::Program, + _program: &mut backend::ast::Program, first_pass: &FirstPassRecord<'src>, - ns_names: NamespaceNames<'src> + (self_name, module): (&'src str, &mut backend::ast::Module), ) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } - let imported_fn = match first_pass.create_namespace_operation( + for import_function in first_pass.create_namespace_operation( &self.args.body.list, self.identifier.as_ref().map(|id| id.0), &self.return_type, - ns_names.js_name, + self_name, util::throws(&self.attributes) ) { - Some(f) => f, - None => { return Ok(()) } + module.imports.push( + backend::ast::Import { + module: None, + js_namespace: Some(raw_ident(self_name)), + kind: backend::ast::ImportKind::Function(import_function), + } + ); }; - let import = backend::ast::Import { - module: None, - js_namespace: Some(raw_ident(ns_names.js_name)), - kind: backend::ast::ImportKind::Function(imported_fn), - }; - - program - .modules - .get_mut(ns_names.rust_name) - .unwrap() - .imports - .push(import); - Ok(()) } } - diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 2754ea7a..0722d2c8 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1,4 +1,5 @@ use std::iter::FromIterator; +use std::collections::BTreeMap; use backend; use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident}; @@ -7,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::{self, 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, @@ -38,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)))) @@ -225,155 +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 weedle::types::Type<'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, - }; - - 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, - }; - - 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), - - // Support for these types is not yet implemented, so skip - // generating any bindings for this function. - | DataView(_) - | Error(_) - | FrozenArrayType(_) - | Promise(_) - | RecordType(..) - | Sequence(_) - | Symbol(_) => { - 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}; @@ -471,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), @@ -479,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(), @@ -497,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(), @@ -519,393 +195,176 @@ 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); -} - -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> TypeToString for weedle::types::ReturnType<'src> { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { - match self { - weedle::types::ReturnType::Type(ty) => (*ty).type_to_string(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) { - 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 TypeToString for weedle::term::Byte { - fn type_to_string(&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) { - dst.push_str("u8"); - } -} - -impl TypeToString for weedle::term::Boolean { - fn type_to_string(&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) { - dst.push_str("usv_str"); - } -} - -impl TypeToString for weedle::term::ByteString { - fn type_to_string(&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) { - dst.push_str("dom_str"); - } -} - -impl TypeToString for weedle::term::Float32Array { - fn type_to_string(&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) { - dst.push_str("f64_array"); - } -} - -impl TypeToString for weedle::term::Int8Array { - fn type_to_string(&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) { - dst.push_str("i16_array"); - } -} - -impl TypeToString for weedle::term::Int32Array { - fn type_to_string(&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) { - dst.push_str("u8_array"); - } -} - -impl TypeToString for weedle::term::Uint8ClampedArray { - fn type_to_string(&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) { - dst.push_str("u16_array"); - } -} - -impl TypeToString for weedle::term::Uint32Array { - fn type_to_string(&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) { - match record.typedefs.get(self.0) { - Some(other) => other.type_to_string(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) { - 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 TypeToString for FloatingPointType { - fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { - match self { - FloatingPointType::Float(_) => dst.push_str("f32"), - FloatingPointType::Double(_) => dst.push_str("f64"), - } - } -} - -impl TypeToString for weedle::term::ArrayBuffer { - fn type_to_string(&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) { - dst.push_str("symbol"); - } -} - -impl TypeToString for weedle::term::Object { - fn type_to_string(&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) { - dst.push_str("data_view"); - } -} - -impl TypeToString for weedle::term::Error { - fn type_to_string(&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> 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> 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> TypeToString for weedle::types::RecordType<'src> { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { - dst.push_str("record_from_"); - self.generics.body.0.type_to_string(record, dst); - dst.push_str("_to_"); - self.generics.body.2.type_to_string(record, dst); - } -} - -impl<'a> TypeToString for weedle::types::Type<'a> { - fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { - use weedle::types::NonAnyType::*; - - let single = match self { - Type::Single(s) => s, - Type::Union(_) => panic!("unions not supported"), - }; - - let ty = match single { - SingleType::Any(_) => return dst.push_str("any"), - SingleType::NonAny(other) => other, - }; - - 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), - - 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), - - 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), - - 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<'src> FirstPassRecord<'src> { - /// Use 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. - fn webidl_arguments_to_syn_arg_captured( - &self, - arguments: &[Argument], - kind: &backend::ast::ImportFunctionKind, - ) -> 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)) - } - } - } - - Some(res) - } - /// Create a wasm-bindgen function, if possible. - /// - /// Currently fails on any variadic args. 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 - { - 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()); - } + ) -> Vec { + let rust_name = if overloaded && !arguments.is_empty() { + let mut argument_type_names = String::new(); + for argument in arguments { + 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 { + argument.1.push_type_name(&mut argument_type_names); } else { - name.to_snake_case() + "_with_" + &argument_type_names + argument_type_names.push_str(&argument.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 ret = match ret { + IdlType::Void => None, + ret @ _ => { + match ret.to_syn_type(TypePosition::Return) { + None => { + warn!("Can not convert return type to syn type: {:?}", ret); + 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 shim = { - let ns = match kind { - backend::ast::ImportFunctionKind::Normal => "", - backend::ast::ImportFunctionKind::Method { ref class, .. } => class, - }; - - raw_ident(&format!("__widl_f_{}_{}", rust_name, ns)) + let ret = if catch { + Some(ret.map_or_else(|| result_ty(unit_ty()), result_ty)) + } else { + ret }; - 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, - }) + 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 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_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 + } 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)) + }; + + 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 { + warn!("Can not convert argument type to syn type: {:?}", idl_type); + 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: args_captured, + 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(), + }) + } + 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. @@ -918,11 +377,12 @@ impl<'src> FirstPassRecord<'src> { is_static: bool, structural: bool, catch: bool, - ) -> Option { - let (overloaded, same_argument_names) = self.get_method_overloading( + ) -> Vec { + let (overloaded, same_argument_names) = self.get_operation_overloading( arguments, &operation_id, self_name, + false, ); let name = match &operation_id { @@ -930,7 +390,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(), }, @@ -954,18 +414,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( @@ -980,11 +433,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, @@ -995,30 +453,61 @@ impl<'src> FirstPassRecord<'src> { /// Whether operation is overloaded and /// whether there overloads with same argument names for given argument types - pub fn get_method_overloading( + pub fn get_operation_overloading( &self, arguments: &[weedle::argument::Argument], - id: &first_pass::OperationId, + operation_id: &first_pass::OperationId, self_name: &str, + namespace: bool, ) -> (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, + operation_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(operation_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, operation_id, self_name, mixin_name) { + return Some(operation_data); + } + } + } + None + } + + let operation_data = if !namespace { + self + .interfaces + .get(self_name) + .and_then(|interface_data| interface_data.operations.get(operation_id)) + .unwrap_or_else(|| + get_operation_data(self, operation_id, self_name, self_name) + .expect(&format!("not found operation {:?} in interface {}", operation_id, self_name)) + ) + } else { + self + .namespaces + .get(self_name) + .and_then(|interface_data| interface_data.operations.get(operation_id)) + .expect(&format!("not found operation {:?} in namespace {}", operation_id, self_name)) }; + let mut names = Vec::with_capacity(arguments.len()); - for arg in arguments { - match arg { - Argument::Single(arg) => names.push(arg.identifier.0), - Argument::Variadic(_) => return (false, false), + for argument in arguments { + match argument { + Argument::Single(single) => names.push(single.identifier.0), + Argument::Variadic(variadic) => names.push(variadic.identifier.0), } } ( - data.overloaded, - *data + operation_data.overloaded, + *operation_data .argument_names_same .get(&names) .unwrap_or(&false) @@ -1031,82 +520,53 @@ impl<'src> FirstPassRecord<'src> { arguments: &[weedle::argument::Argument], operation_name: Option<&str>, return_type: &weedle::types::ReturnType, - namespace_name: &str, + self_name: &str, catch: bool, - ) -> Option { - let (overloaded, same_argument_names) = self.get_namespaced_operation_overloading( + ) -> Vec { + let (overloaded, same_argument_names) = self.get_operation_overloading( arguments, - operation_name, - namespace_name, + &first_pass::OperationId::Operation(operation_name), + self_name, + true, ); let name = match operation_name { Some(name) => name.to_string(), None => { warn!("Operations without a name are unsupported"); - return None; + return Vec::new(); } }; - 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 = Some( + format!( + "The `{}.{}()` function\n\n{}", + self_name, + name, + mdn_doc(self_name, Some(&name)) + ) + ); + + let arguments = match self.convert_arguments(arguments) { + None => return Vec::new(), + Some(arguments) => arguments }; - let doc_comment = format!("The `{}.{}()` function\n\n{}", - namespace_name, - name, - mdn_doc(namespace_name, Some(&name))); // checked link self.create_function( &name, overloaded, same_argument_names, - arguments, + &arguments, ret, backend::ast::ImportFunctionKind::Normal, false, catch, - Some(doc_comment), - ) - } - - /// Whether operation is overloaded and - /// whether there overloads with same argument names for given argument types - pub fn get_namespaced_operation_overloading( - &self, - arguments: &[weedle::argument::Argument], - operation_name: Option<&str>, - namespace_name: &str, - ) -> (bool, bool) { - let data = match self.namespaces.get(namespace_name) { - Some(data) => data, - None => return (false, false), - }; - let data = match data.operations.get(&operation_name) { - Some(data) => data, - None => return (false, false), - }; - let mut names = Vec::with_capacity(arguments.len()); - for arg in arguments { - match arg { - Argument::Single(arg) => names.push(arg.identifier.0), - Argument::Variadic(_) => return (false, false), - } - } - ( - data.overloaded, - *data - .argument_names_same - .get(&names) - .unwrap_or(&false) + doc_comment, ) } @@ -1119,13 +579,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 { @@ -1150,7 +607,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())), @@ -1165,17 +622,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,