From e92374a8c8f73777538bdf25a804ae5581ec0bb0 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Sat, 11 Aug 2018 23:46:33 +0300 Subject: [PATCH] Migrate to IdlType --- crates/webidl/src/idl_type.rs | 675 +++++++++++++++++++++++ crates/webidl/src/lib.rs | 137 +++-- crates/webidl/src/util.rs | 988 +++++----------------------------- 3 files changed, 871 insertions(+), 929 deletions(-) create mode 100644 crates/webidl/src/idl_type.rs diff --git a/crates/webidl/src/idl_type.rs b/crates/webidl/src/idl_type.rs new file mode 100644 index 00000000..7d4cd81e --- /dev/null +++ b/crates/webidl/src/idl_type.rs @@ -0,0 +1,675 @@ +use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident}; +use heck::SnakeCase; +use syn; +use weedle::common::Identifier; +use weedle::term; +use weedle::types::*; + +use first_pass::FirstPassRecord; +use util::{TypePosition, camel_case_ident, shared_ref, option_ty, array}; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] +pub(crate) enum IdlType<'a> { + Boolean, + Byte, + Octet, + Short, + UnsignedShort, + Long, + UnsignedLong, + LongLong, + UnsignedLongLong, + Float, + UnrestrictedFloat, + Double, + UnrestrictedDouble, + DomString, + ByteString, + UsvString, + Object, + Symbol, + Error, + + ArrayBuffer, + DataView, + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + + Interface(&'a str), + Dictionary(&'a str), + Enum(&'a str), + + Nullable(Box>), + FrozenArray(Box>), + Sequence(Box>), + Promise(Box>), + Record(Box>, Box>), + Union(Vec>), + + Any, + Void, +} + +pub(crate) trait ToIdlType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option>; +} + +impl<'a> ToIdlType<'a> for UnionType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + let mut idl_types = Vec::with_capacity(self.body.list.len()); + for t in &self.body.list { + idl_types.push(t.to_idl_type(record)?); + } + Some(IdlType::Union(idl_types)) + } +} + +impl<'a> ToIdlType<'a> for Type<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + Type::Single(t) => t.to_idl_type(record), + Type::Union(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for SingleType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + SingleType::Any(t) => t.to_idl_type(record), + SingleType::NonAny(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for NonAnyType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + NonAnyType::Promise(t) => t.to_idl_type(record), + NonAnyType::Integer(t) => t.to_idl_type(record), + NonAnyType::FloatingPoint(t) => t.to_idl_type(record), + NonAnyType::Boolean(t) => t.to_idl_type(record), + NonAnyType::Byte(t) => t.to_idl_type(record), + NonAnyType::Octet(t) => t.to_idl_type(record), + NonAnyType::ByteString(t) => t.to_idl_type(record), + NonAnyType::DOMString(t) => t.to_idl_type(record), + NonAnyType::USVString(t) => t.to_idl_type(record), + NonAnyType::Sequence(t) => t.to_idl_type(record), + NonAnyType::Object(t) => t.to_idl_type(record), + NonAnyType::Symbol(t) => t.to_idl_type(record), + NonAnyType::Error(t) => t.to_idl_type(record), + NonAnyType::ArrayBuffer(t) => t.to_idl_type(record), + NonAnyType::DataView(t) => t.to_idl_type(record), + NonAnyType::Int8Array(t) => t.to_idl_type(record), + NonAnyType::Int16Array(t) => t.to_idl_type(record), + NonAnyType::Int32Array(t) => t.to_idl_type(record), + NonAnyType::Uint8Array(t) => t.to_idl_type(record), + NonAnyType::Uint16Array(t) => t.to_idl_type(record), + NonAnyType::Uint32Array(t) => t.to_idl_type(record), + NonAnyType::Uint8ClampedArray(t) => t.to_idl_type(record), + NonAnyType::Float32Array(t) => t.to_idl_type(record), + NonAnyType::Float64Array(t) => t.to_idl_type(record), + NonAnyType::FrozenArrayType(t) => t.to_idl_type(record), + NonAnyType::RecordType(t) => t.to_idl_type(record), + NonAnyType::Identifier(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for SequenceType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + Some(IdlType::Sequence(Box::new(self.generics.body.to_idl_type(record)?))) + } +} + +impl<'a> ToIdlType<'a> for FrozenArrayType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + Some(IdlType::FrozenArray(Box::new(self.generics.body.to_idl_type(record)?))) + } +} + +impl<'a, T: ToIdlType<'a>> ToIdlType<'a> for MayBeNull { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + let inner_idl_type = self.type_.to_idl_type(record)?; + if self.q_mark.is_some() { + Some(IdlType::Nullable(Box::new(inner_idl_type))) + } else { + Some(inner_idl_type) + } + } +} + +impl<'a> ToIdlType<'a> for PromiseType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + Some(IdlType::Promise(Box::new(self.generics.body.to_idl_type(record)?))) + } +} + +impl<'a> ToIdlType<'a> for IntegerType { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + IntegerType::LongLong(t) => t.to_idl_type(record), + IntegerType::Long(t) => t.to_idl_type(record), + IntegerType::Short(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for LongLongType { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + if self.unsigned.is_some() { + Some(IdlType::UnsignedLongLong) + } else { + Some(IdlType::LongLong) + } + } +} + +impl<'a> ToIdlType<'a> for LongType { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + if self.unsigned.is_some() { + Some(IdlType::UnsignedLong) + } else { + Some(IdlType::Long) + } + } +} + +impl<'a> ToIdlType<'a> for ShortType { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + if self.unsigned.is_some() { + Some(IdlType::UnsignedShort) + } else { + Some(IdlType::Short) + } + } +} + +impl<'a> ToIdlType<'a> for FloatingPointType { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + FloatingPointType::Float(t) => t.to_idl_type(record), + FloatingPointType::Double(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for FloatType { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + if self.unrestricted.is_some() { + Some(IdlType::UnrestrictedFloat) + } else { + Some(IdlType::Float) + } + } +} + +impl<'a> ToIdlType<'a> for DoubleType { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + if self.unrestricted.is_some() { + Some(IdlType::UnrestrictedDouble) + } else { + Some(IdlType::Double) + } + } +} + +impl<'a> ToIdlType<'a> for RecordType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + Some( + IdlType::Record( + Box::new(self.generics.body.0.to_idl_type(record)?), + Box::new(self.generics.body.2.to_idl_type(record)?) + ) + ) + } +} + +impl<'a> ToIdlType<'a> for StringType { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + StringType::Byte(t) => t.to_idl_type(record), + StringType::DOM(t) => t.to_idl_type(record), + StringType::USV(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for UnionMemberType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + UnionMemberType::Single(t) => t.to_idl_type(record), + UnionMemberType::Union(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for ConstType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + ConstType::Integer(t) => t.to_idl_type(record), + ConstType::FloatingPoint(t) => t.to_idl_type(record), + ConstType::Boolean(t) => t.to_idl_type(record), + ConstType::Byte(t) => t.to_idl_type(record), + ConstType::Octet(t) => t.to_idl_type(record), + ConstType::Identifier(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for ReturnType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + match self { + ReturnType::Void(t) => t.to_idl_type(record), + ReturnType::Type(t) => t.to_idl_type(record), + } + } +} + +impl<'a> ToIdlType<'a> for AttributedType<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + self.type_.to_idl_type(record) + } +} + +impl<'a> ToIdlType<'a> for Identifier<'a> { + fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option> { + if let Some(idl_type) = record.typedefs.get(&self.0) { + idl_type.to_idl_type(record) + } else if record.interfaces.contains_key(self.0) { + Some(IdlType::Interface(self.0)) + } else if record.dictionaries.contains(self.0) { + Some(IdlType::Dictionary(self.0)) + } else if record.enums.contains(self.0) { + Some(IdlType::Enum(self.0)) + } else { + warn!("unrecognized type {}", self.0); + None + } + } +} + +macro_rules! terms_to_idl_type { + ($($t:tt => $r:tt)*) => ($( + impl<'a> ToIdlType<'a> for term::$t { + fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option> { + Some(IdlType::$r) + } + } + )*) +} + +terms_to_idl_type! { + Symbol => Symbol + ByteString => ByteString + DOMString => DomString + USVString => UsvString + Any => Any + Boolean => Boolean + Byte => Byte + Double => Double + Float => Float + Long => Long + Object => Object + Octet => Octet + Short => Short + Void => Void + ArrayBuffer => ArrayBuffer + DataView => DataView + Int8Array => Int8Array + Int16Array => Int16Array + Int32Array => Int32Array + Uint8Array => Uint8Array + Uint16Array => Uint16Array + Uint32Array => Uint32Array + Uint8ClampedArray => Uint8ClampedArray + Float32Array => Float32Array + Float64Array => Float64Array + Error => Error +} + +impl<'a> IdlType<'a> { + /// Generates a snake case type name. + pub(crate) fn push_type_name(&self, dst: &mut String) { + match self { + IdlType::Boolean => dst.push_str("bool"), + IdlType::Byte => dst.push_str("i8"), + IdlType::Octet => dst.push_str("u8"), + IdlType::Short => dst.push_str("i16"), + IdlType::UnsignedShort => dst.push_str("u16"), + IdlType::Long => dst.push_str("i32"), + IdlType::UnsignedLong => dst.push_str("u32"), + IdlType::LongLong => dst.push_str("i64"), + IdlType::UnsignedLongLong => dst.push_str("u64"), + IdlType::Float => dst.push_str("f32"), + IdlType::UnrestrictedFloat => dst.push_str("unrestricted_f32"), + IdlType::Double => dst.push_str("f64"), + IdlType::UnrestrictedDouble => dst.push_str("unrestricted_f64"), + IdlType::DomString => dst.push_str("dom_str"), + IdlType::ByteString => dst.push_str("byte_str"), + IdlType::UsvString => dst.push_str("usv_str"), + IdlType::Object => dst.push_str("object"), + IdlType::Symbol => dst.push_str("symbol"), + IdlType::Error => dst.push_str("error"), + + IdlType::ArrayBuffer => dst.push_str("array_buffer"), + IdlType::DataView => dst.push_str("data_view"), + IdlType::Int8Array => dst.push_str("i8_array"), + IdlType::Uint8Array => dst.push_str("u8_array"), + IdlType::Uint8ClampedArray => dst.push_str("u8_clamped_array"), + IdlType::Int16Array => dst.push_str("i16_array"), + IdlType::Uint16Array => dst.push_str("u16_array"), + IdlType::Int32Array => dst.push_str("i32_array"), + IdlType::Uint32Array => dst.push_str("u32_array"), + IdlType::Float32Array => dst.push_str("f32_array"), + IdlType::Float64Array => dst.push_str("f64_array"), + + IdlType::Interface(name) => dst.push_str(&name.to_snake_case()), + IdlType::Dictionary(name) => dst.push_str(&name.to_snake_case()), + IdlType::Enum(name) => dst.push_str(&name.to_snake_case()), + + IdlType::Nullable(idl_type) => { + dst.push_str("opt_"); + idl_type.push_type_name(dst); + }, + IdlType::FrozenArray(idl_type) => { + idl_type.push_type_name(dst); + dst.push_str("_frozen_array"); + }, + IdlType::Sequence(idl_type) => { + idl_type.push_type_name(dst); + dst.push_str("_sequence"); + }, + IdlType::Promise(idl_type) => { + idl_type.push_type_name(dst); + dst.push_str("_promise"); + }, + IdlType::Record(idl_type_from, idl_type_to) => { + dst.push_str("record_from_"); + idl_type_from.push_type_name(dst); + dst.push_str("_to_"); + idl_type_to.push_type_name(dst); + }, + IdlType::Union(idl_types) => { + dst.push_str("union_of_"); + let mut first = true; + for idl_type in idl_types { + if first { + first = false; + } else { + dst.push_str("_and_"); + } + idl_type.push_type_name(dst); + } + }, + + IdlType::Any => dst.push_str("any"), + IdlType::Void => dst.push_str("void"), + } + } + + /// Generates a snake case type name. + #[allow(dead_code)] + pub(crate) fn get_type_name(&self) -> String { + let mut string = String::new(); + self.push_type_name(&mut string); + return string; + } + + /// Converts to syn type if possible. + pub(crate) fn to_syn_type(&self, pos: TypePosition) -> Option { + match self { + IdlType::Boolean => Some(ident_ty(raw_ident("bool"))), + IdlType::Byte => Some(ident_ty(raw_ident("i8"))), + IdlType::Octet => Some(ident_ty(raw_ident("u8"))), + IdlType::Short => Some(ident_ty(raw_ident("i16"))), + IdlType::UnsignedShort => Some(ident_ty(raw_ident("u16"))), + IdlType::Long => Some(ident_ty(raw_ident("i32"))), + IdlType::UnsignedLong => Some(ident_ty(raw_ident("u32"))), + IdlType::LongLong => Some(ident_ty(raw_ident("i64"))), + IdlType::UnsignedLongLong => Some(ident_ty(raw_ident("u64"))), + IdlType::Float => Some(ident_ty(raw_ident("f32"))), + IdlType::UnrestrictedFloat => Some(ident_ty(raw_ident("f32"))), + IdlType::Double => Some(ident_ty(raw_ident("f64"))), + IdlType::UnrestrictedDouble => Some(ident_ty(raw_ident("f64"))), + | IdlType::DomString + | IdlType::ByteString + | IdlType::UsvString => match pos { + TypePosition::Argument => Some(shared_ref(ident_ty(raw_ident("str")))), + TypePosition::Return => Some(ident_ty(raw_ident("String"))), + }, + IdlType::Object => { + let path = vec![rust_ident("js_sys"), rust_ident("Object")]; + Some(leading_colon_path_ty(path)) + }, + IdlType::Symbol => None, + IdlType::Error => None, + + IdlType::ArrayBuffer => { + let path = vec![rust_ident("js_sys"), rust_ident("ArrayBuffer")]; + Some(leading_colon_path_ty(path)) + }, + IdlType::DataView => None, + IdlType::Int8Array => Some(array("i8", pos)), + IdlType::Uint8Array => Some(array("u8", pos)), + IdlType::Uint8ClampedArray => Some(array("u8", pos)), + IdlType::Int16Array => Some(array("i16", pos)), + IdlType::Uint16Array => Some(array("u16", pos)), + IdlType::Int32Array => Some(array("i32", pos)), + IdlType::Uint32Array => Some(array("u32", pos)), + IdlType::Float32Array => Some(array("f32", pos)), + IdlType::Float64Array => Some(array("f64", pos)), + + IdlType::Interface(name) => { + let ty = ident_ty(rust_ident(camel_case_ident(name).as_str())); + if pos == TypePosition::Argument { + Some(shared_ref(ty)) + } else { + Some(ty) + } + }, + IdlType::Dictionary(name) => Some(ident_ty(rust_ident(camel_case_ident(name).as_str()))), + IdlType::Enum(name) => Some(ident_ty(rust_ident(camel_case_ident(name).as_str()))), + + IdlType::Nullable(idl_type) => Some(option_ty(idl_type.to_syn_type(pos)?)), + IdlType::FrozenArray(_idl_type) => None, + IdlType::Sequence(_idl_type) => None, + IdlType::Promise(_idl_type) => None, + IdlType::Record(_idl_type_from, _idl_type_to) => None, + IdlType::Union(_idl_types) => None, + + IdlType::Any => { + let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; + Some(leading_colon_path_ty(path)) + }, + IdlType::Void => None, + } + } + + /// Flattens unions recursively. + /// + /// Works similarly to [flattened union member types], + /// but also flattens unions inside generics of other types. + /// + /// [flattened union member types]: https://heycam.github.io/webidl/#dfn-flattened-union-member-types + pub(crate) fn flatten(&self) -> Vec { + match self { + IdlType::Nullable(idl_type) => idl_type + .flatten() + .into_iter() + .map(Box::new) + .map(IdlType::Nullable) + .collect(), + IdlType::FrozenArray(idl_type) => idl_type + .flatten() + .into_iter() + .map(Box::new) + .map(IdlType::FrozenArray) + .collect(), + IdlType::Sequence(idl_type) => idl_type + .flatten() + .into_iter() + .map(Box::new) + .map(IdlType::Sequence) + .collect(), + IdlType::Promise(idl_type) => idl_type + .flatten() + .into_iter() + .map(Box::new) + .map(IdlType::Promise) + .collect(), + IdlType::Record(idl_type_from, idl_type_to) => { + let mut idl_types = Vec::new(); + for idl_type_from in idl_type_from.flatten() { + for idl_type_to in idl_type_to.flatten() { + idl_types.push( + IdlType::Record( + Box::new(idl_type_from.clone()), + Box::new(idl_type_to.clone()) + ) + ); + } + } + idl_types + }, + IdlType::Union(idl_types) => idl_types + .iter() + .flat_map(|idl_type| idl_type.flatten()) + .collect(), + + idl_type @ _ => vec![idl_type.clone()] + } + } +} + +#[test] +fn idl_type_flatten_test() { + use self::IdlType::*; + + assert_eq!( + Union(vec![ + Interface("Node"), + Union(vec![ + Sequence( + Box::new(Long), + ), + Interface("Event"), + ]), + Nullable( + Box::new(Union(vec![ + Interface("XMLHttpRequest"), + DomString, + ])), + ), + Sequence( + Box::new(Union(vec![ + Sequence( + Box::new(Double), + ), + Interface("NodeList"), + ])), + ), + ]).flatten(), + vec![ + Interface("Node"), + Sequence(Box::new(Long)), + Interface("Event"), + Nullable(Box::new(Interface("XMLHttpRequest"))), + Nullable(Box::new(DomString)), + Sequence(Box::new(Sequence(Box::new(Double)))), + Sequence(Box::new(Interface("NodeList"))), + ], + ); +} + +/// Converts arguments into possibilities. +/// +/// Each argument represented with a tuple of its idl type and whether it is optional. +/// Each possibility is a vector of idl types. +/// +/// The goal is to find equivalent possibilities of argument types each of which is not optional and +/// does not contains union types. +pub(crate) fn flatten<'a>(arguments: &'a [(IdlType, bool)]) -> Vec>> { + if arguments.is_empty() { + return vec![Vec::new()]; + } + let mut optional_possibilities = if arguments[0].1 { vec![Vec::new()] } else { Vec::new() }; + let mut possibilities = Vec::new(); + for idl_type in arguments[0].0.flatten() { + possibilities.push(vec![idl_type]) + } + for argument in arguments[1..].iter() { + let mut new_possibilities = Vec::new(); + for old_idl_types in possibilities { + if argument.1 { + optional_possibilities.push(old_idl_types.clone()); + } + for idl_type in argument.0.flatten() { + let mut new_idl_types = old_idl_types.clone(); + new_idl_types.push(idl_type); + new_possibilities.push(new_idl_types) + } + } + possibilities = new_possibilities; + } + optional_possibilities.extend(possibilities.into_iter()); + optional_possibilities +} + +#[test] +fn arguments_flatten_test() { + use self::IdlType::*; + + assert_eq!( + flatten( + &vec![ + ( + Union(vec![ + Short, + Long, + ]), + false, + ), + ( + Union(vec![ + Sequence(Box::new( + Union(vec![ + Byte, + Octet, + ]), + )), + LongLong, + ]), + true, + ), + ( + DomString, + true, + ) + ] + ), + vec![ + vec![Short], + vec![Long], + vec![Short, Sequence(Box::new(Byte))], + vec![Short, Sequence(Box::new(Octet))], + vec![Short, LongLong], + vec![Long, Sequence(Box::new(Byte))], + vec![Long, Sequence(Box::new(Octet))], + vec![Long, LongLong], + vec![Short, Sequence(Box::new(Byte)), DomString], + vec![Short, Sequence(Box::new(Octet)), DomString], + vec![Short, LongLong, DomString], + vec![Long, Sequence(Box::new(Byte)), DomString], + vec![Long, Sequence(Box::new(Octet)), DomString], + vec![Long, LongLong, DomString] + ], + ); +} diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 1471dc46..fc1f2f0e 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -25,6 +25,7 @@ extern crate wasm_bindgen_backend as backend; extern crate weedle; mod first_pass; +mod idl_type; mod util; mod error; @@ -45,7 +46,7 @@ use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList}; use first_pass::{FirstPass, FirstPassRecord}; use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc}; -use util::ToSynType; +use idl_type::{IdlType, ToIdlType}; pub use error::{Error, ErrorKind, Result}; @@ -341,23 +342,22 @@ impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for Extend // > exception**. let throws = true; - first_pass - .create_function( - "new", - overloaded, - same_argument_names, - arguments, - Some(self_ty), - kind, - structural, - throws, - None, - ) - .map(|import_functions| - for import_function in import_functions { - program.imports.push(wrap_import_function(import_function)); - } - ); + for import_function in first_pass.create_function( + "new", + overloaded, + same_argument_names, + &match first_pass.convert_arguments(arguments) { + None => return, + Some(arguments) => arguments + }, + IdlType::Interface(interface.identifier.0), + kind, + structural, + throws, + None, + ) { + program.imports.push(wrap_import_function(import_function)); + } }; match self { @@ -566,36 +566,28 @@ fn member_attribute<'src>( let is_structural = util::is_structural(attrs); let throws = util::throws(attrs); - first_pass - .create_getter( + for import_function in first_pass.create_getter( identifier, &type_.type_, self_name, is_static, is_structural, throws, - ) - .map(|import_functions| - for import_function in import_functions { - program.imports.push(wrap_import_function(import_function)); - } - ); + ) { + program.imports.push(wrap_import_function(import_function)); + } if !readonly { - first_pass - .create_setter( - identifier, - type_.type_.clone(), - self_name, - is_static, - is_structural, - throws, - ) - .map(|import_functions| - for import_function in import_functions { - program.imports.push(wrap_import_function(import_function)); - } - ); + for import_function in first_pass.create_setter( + identifier, + type_.type_.clone(), + self_name, + is_static, + is_structural, + throws, + ) { + program.imports.push(wrap_import_function(import_function)); + } } Ok(()) @@ -669,38 +661,34 @@ fn member_operation<'src>( None => false, }; - first_pass - .create_basic_method( - args, - match identifier.map(|s| s.0) { - None if specials.is_empty() => ::first_pass::OperationId::Operation(None), - None if specials.len() == 1 => match specials[0] { - weedle::interface::Special::Getter(weedle::term::Getter) => ::first_pass::OperationId::IndexingGetter, - weedle::interface::Special::Setter(weedle::term::Setter) => ::first_pass::OperationId::IndexingSetter, - weedle::interface::Special::Deleter(weedle::term::Deleter) => ::first_pass::OperationId::IndexingDeleter, - weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller) => return Ok(()), - }, - Some(ref name) if specials.is_empty() => ::first_pass::OperationId::Operation(Some(name.clone())), - _ => { - warn!("Unsupported specials on type {:?}", (self_name, identifier)); - return Ok(()) - } + for import_function in first_pass.create_basic_method( + args, + match identifier.map(|s| s.0) { + None if specials.is_empty() => ::first_pass::OperationId::Operation(None), + None if specials.len() == 1 => match specials[0] { + weedle::interface::Special::Getter(weedle::term::Getter) => ::first_pass::OperationId::IndexingGetter, + weedle::interface::Special::Setter(weedle::term::Setter) => ::first_pass::OperationId::IndexingSetter, + weedle::interface::Special::Deleter(weedle::term::Deleter) => ::first_pass::OperationId::IndexingDeleter, + weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller) => return Ok(()), }, - return_type, - self_name, - is_static, - specials.len() == 1 || first_pass - .interfaces - .get(self_name) - .map(|interface_data| interface_data.global) - .unwrap_or(false), - util::throws(attrs), - ) - .map(|import_functions| - for import_function in import_functions { - program.imports.push(wrap_import_function(import_function)); + Some(ref name) if specials.is_empty() => ::first_pass::OperationId::Operation(Some(name.clone())), + _ => { + warn!("Unsupported specials on type {:?}", (self_name, identifier)); + return Ok(()) } - ); + }, + return_type, + self_name, + is_static, + specials.len() == 1 || first_pass + .interfaces + .get(self_name) + .map(|interface_data| interface_data.global) + .unwrap_or(false), + util::throws(attrs), + ) { + program.imports.push(wrap_import_function(import_function)); + } Ok(()) } @@ -796,9 +784,14 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> return Ok(()); } - let ty = match self.const_type.to_syn_type(record, TypePosition::Return) { - Some(s) => s, + let idl_type = match self.const_type.to_idl_type(record) { None => return Ok(()), + Some(idl_type) => idl_type, + }; + + let ty = match idl_type.to_syn_type(TypePosition::Return) { + None => return Ok(()), + Some(ty) => ty, }; program.consts.push(backend::ast::Const { diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 80d19216..d94c3e01 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1,5 +1,4 @@ use std::iter::FromIterator; -use std::iter; use std::collections::BTreeMap; use backend; @@ -9,15 +8,14 @@ use proc_macro2::Ident; use syn; use weedle; use weedle::attribute::{ExtendedAttributeList, ExtendedAttribute}; -use weedle::argument::{Argument, SingleArgument}; -use weedle::common::Identifier; -use weedle::types::*; +use weedle::argument::Argument; use weedle::literal::{ConstValue, FloatLit, IntegerLit}; use first_pass::FirstPassRecord; +use idl_type::{IdlType, ToIdlType, flatten}; /// Take a type and create an immutable shared reference to that type. -fn shared_ref(ty: syn::Type) -> syn::Type { +pub(crate) fn shared_ref(ty: syn::Type) -> syn::Type { syn::TypeReference { and_token: Default::default(), lifetime: None, @@ -40,183 +38,8 @@ pub fn mdn_doc(class: &str, method: Option<&str>) -> String { format!("[Documentation]({})", link).into() } -pub(crate) trait ToSynType<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option; -} - -impl<'src, T: ToSynType<'src>> ToSynType<'src> for MayBeNull { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - let ty = self.type_.to_syn_type(record, pos)?; - if self.q_mark.is_some() { - Some(option_ty(ty)) - } else { - Some(ty) - } - } -} - -impl<'src> ToSynType<'src> for ConstType<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - match self { - ConstType::Integer(l) => l.to_syn_type(record, pos), - ConstType::FloatingPoint(l) => l.to_syn_type(record, pos), - ConstType::Boolean(l) => l.to_syn_type(record, pos), - ConstType::Byte(l) => l.to_syn_type(record, pos), - ConstType::Octet(l) => l.to_syn_type(record, pos), - ConstType::Identifier(l) => l.to_syn_type(record, pos), - } - } -} - -impl<'src> ToSynType<'src> for IntegerType { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - match self { - IntegerType::LongLong(l) => l.to_syn_type(record, pos), - IntegerType::Long(l) => l.to_syn_type(record, pos), - IntegerType::Short(l) => l.to_syn_type(record, pos), - } - } -} - -impl<'src> ToSynType<'src> for ShortType { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - if self.unsigned.is_some() { - Some(ident_ty(raw_ident("u16"))) - } else { - Some(ident_ty(raw_ident("i16"))) - } - } -} - -impl<'src> ToSynType<'src> for LongType { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - if self.unsigned.is_some() { - Some(ident_ty(raw_ident("u32"))) - } else { - Some(ident_ty(raw_ident("i32"))) - } - } -} - -impl<'src> ToSynType<'src> for LongLongType { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - if self.unsigned.is_some() { - Some(ident_ty(raw_ident("u64"))) - } else { - Some(ident_ty(raw_ident("i64"))) - } - } -} - -impl<'src> ToSynType<'src> for FloatingPointType { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - match self { - FloatingPointType::Float(_) => Some(ident_ty(raw_ident("f32"))), - FloatingPointType::Double(_) => Some(ident_ty(raw_ident("f64"))), - } - } -} - -impl<'src> ToSynType<'src> for weedle::term::Boolean { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - Some(ident_ty(raw_ident("bool"))) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Byte { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - Some(ident_ty(raw_ident("i8"))) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Octet { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - Some(ident_ty(raw_ident("u8"))) - } -} - -impl<'src> ToSynType<'src> for weedle::common::Identifier<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - if let Some(other) = record.typedefs.get(&self.0) { - return other.to_syn_type(record, pos) - } - // A reference to a type by name becomes the same thing in the - // bindings. - let ty = ident_ty(rust_ident(camel_case_ident(self.0).as_str())); - Some(if record.interfaces.contains_key(self.0) { - if pos == TypePosition::Argument { - shared_ref(ty) - } else { - ty - } - } else if record.dictionaries.contains(self.0) { - ty - } else if record.enums.contains(self.0) { - ty - } else { - warn!("unrecognized type {}", self.0); - ty - }) - } -} - -impl<'src> ToSynType<'src> for weedle::term::DOMString { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - // strings -> `&str` for arguments and `String` for return - Some(match pos { - TypePosition::Argument => shared_ref(ident_ty(raw_ident("str"))), - TypePosition::Return => ident_ty(raw_ident("String")), - }) - } -} - -impl<'src> ToSynType<'src> for weedle::term::ByteString { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - // ByteString maps to String in JS - - // https://developer.mozilla.org/en-US/docs/Web/API/ByteString - weedle::term::DOMString.to_syn_type(record, pos) - } -} - -impl<'src> ToSynType<'src> for weedle::term::USVString { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - // USVString maps to String in JS - - // https://developer.mozilla.org/en-US/docs/Web/API/USVString - weedle::term::DOMString.to_syn_type(record, pos) - } -} - // Array type is borrowed for arguments (`&[T]`) and owned for return value (`Vec`). -fn array(base_ty: &str, pos: TypePosition) -> syn::Type { +pub(crate) fn array(base_ty: &str, pos: TypePosition) -> syn::Type { match pos { TypePosition::Argument => { shared_ref(slice_ty(ident_ty(raw_ident(base_ty)))) @@ -227,166 +50,6 @@ fn array(base_ty: &str, pos: TypePosition) -> syn::Type { } } -impl<'src> ToSynType<'src> for weedle::term::Float32Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("f32", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Float64Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("f64", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Int8Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("i8", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Int16Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("i16", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Int32Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("i32", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Uint8Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("u8", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Uint8ClampedArray { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("u8", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Uint16Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("u16", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Uint32Array { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - Some(array("u32", pos)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::ArrayBuffer { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - let path = vec![rust_ident("js_sys"), rust_ident("ArrayBuffer")]; - Some(leading_colon_path_ty(path)) - } -} - -impl<'src> ToSynType<'src> for weedle::term::Object { - fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) - -> Option - { - let path = vec![rust_ident("js_sys"), rust_ident("Object")]; - Some(leading_colon_path_ty(path)) - } -} - -impl<'src> ToSynType<'src> for NonAnyType<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - match self { - NonAnyType::Boolean(s) => s.to_syn_type(record, pos), - NonAnyType::Octet(s) => s.to_syn_type(record, pos), - NonAnyType::Byte(s) => s.to_syn_type(record, pos), - NonAnyType::Identifier(s) => s.to_syn_type(record, pos), - NonAnyType::Integer(s) => s.to_syn_type(record, pos), - NonAnyType::FloatingPoint(s) => s.to_syn_type(record, pos), - - NonAnyType::Float32Array(s) => s.to_syn_type(record, pos), - NonAnyType::Float64Array(s) => s.to_syn_type(record, pos), - NonAnyType::Int8Array(s) => s.to_syn_type(record, pos), - NonAnyType::Int16Array(s) => s.to_syn_type(record, pos), - NonAnyType::Int32Array(s) => s.to_syn_type(record, pos), - NonAnyType::Uint8Array(s) => s.to_syn_type(record, pos), - NonAnyType::Uint8ClampedArray(s) => s.to_syn_type(record, pos), - NonAnyType::Uint16Array(s) => s.to_syn_type(record, pos), - NonAnyType::Uint32Array(s) => s.to_syn_type(record, pos), - - NonAnyType::DOMString(s) => s.to_syn_type(record, pos), - NonAnyType::ByteString(s) => s.to_syn_type(record, pos), - NonAnyType::USVString(s) => s.to_syn_type(record, pos), - NonAnyType::ArrayBuffer(b) => b.to_syn_type(record, pos), - NonAnyType::Object(o) => o.to_syn_type(record, pos), - - // Support for these types is not yet implemented, so skip - // generating any bindings for this function. - | NonAnyType::DataView(_) - | NonAnyType::Error(_) - | NonAnyType::FrozenArrayType(_) - | NonAnyType::Promise(_) - | NonAnyType::RecordType(..) - | NonAnyType::Sequence(_) - | NonAnyType::Symbol(_) => { - None - } - } - } -} - -impl<'src> ToSynType<'src> for SingleType<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - match self { - // `any` becomes `::wasm_bindgen::JsValue`. - SingleType::Any(_) => { - let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; - Some(leading_colon_path_ty(path)) - } - SingleType::NonAny(non_any) => non_any.to_syn_type(record, pos), - } - } -} - -impl<'src> ToSynType<'src> for Type<'src> { - fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) - -> Option - { - match self { - Type::Single(single) => single.to_syn_type(record, pos), - Type::Union(_) => None, - } - } -} - - /// Map a webidl const value to the correct wasm-bindgen const value pub fn webidl_const_v_to_backend_const_v(v: &ConstValue) -> backend::ast::ConstValue { use std::f64::{NEG_INFINITY, INFINITY, NAN}; @@ -484,7 +147,7 @@ fn result_ty(t: syn::Type) -> syn::Type { } /// From `T` create `[T]`. -fn slice_ty(t: syn::Type) -> syn::Type { +pub(crate) fn slice_ty(t: syn::Type) -> syn::Type { syn::TypeSlice { bracket_token: Default::default(), elem: Box::new(t), @@ -492,7 +155,7 @@ fn slice_ty(t: syn::Type) -> syn::Type { } /// From `T` create `Vec`. -fn vec_ty(t: syn::Type) -> syn::Type { +pub(crate) fn vec_ty(t: syn::Type) -> syn::Type { let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { colon2_token: None, lt_token: Default::default(), @@ -510,7 +173,7 @@ fn vec_ty(t: syn::Type) -> syn::Type { } /// From `T` create `Option` -fn option_ty(t: syn::Type) -> syn::Type { +pub(crate) fn option_ty(t: syn::Type) -> syn::Type { let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { colon2_token: None, lt_token: Default::default(), @@ -532,469 +195,30 @@ pub enum TypePosition { Return, } -/// Implemented on an AST type node to get equivalent list of syn types and type names that do not have unions. -/// For example, it turns `Promise<(sequence or short)>` into -/// corresponding syn types and type names of `[Promise>, Promise]`. -trait GetArgumentPossibilities<'src> { - /// Returns option that contains a value if the conversion succeeds. - /// The value is a vector of argument possibilities. - /// Each possibility is a tuple of converted argument (syn) types and type names - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option>; -} - -impl<'src, T: GetArgumentPossibilities<'src>> GetArgumentPossibilities<'src> for MayBeNull { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - Some( - self - .type_ - .get_argument_possibilities(record)? - .into_iter() - .map(|(ty, type_name)| - if self.q_mark.is_some() { - (option_ty(ty), "opt_".to_string() + &type_name) - } else { - (ty, type_name) - } - ) - .collect() - ) - } -} - -impl<'src> GetArgumentPossibilities<'src> for weedle::common::Identifier<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - if let Some(other) = record.typedefs.get(&self.0) { - other.get_argument_possibilities(record) - } else { - let syn_type = self.to_syn_type(record, TypePosition::Argument)?; - let type_name = self.get_type_name(record); - Some(vec![(syn_type, type_name)]) - } - } -} - -impl<'src> GetArgumentPossibilities<'src> for NonAnyType<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - if let NonAnyType::Identifier(identifier) = self { - identifier.get_argument_possibilities(record) - } else { - let syn_type = self.to_syn_type(record, TypePosition::Argument)?; - let type_name = self.get_type_name(record); - Some(vec![(syn_type, type_name)]) - } - } -} - -impl<'src> GetArgumentPossibilities<'src> for SingleType<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - if let SingleType::NonAny(non_any) = self { - non_any.get_argument_possibilities(record) - } else { - let syn_type = self.to_syn_type(record, TypePosition::Argument)?; - let type_name = self.get_type_name(record); - Some(vec![(syn_type, type_name)]) - } - } -} - -impl<'src> GetArgumentPossibilities<'src> for UnionMemberType<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - match self { - UnionMemberType::Single(single) => single.get_argument_possibilities(record), - UnionMemberType::Union(union) => union.get_argument_possibilities(record), - } - } -} - -impl<'src> GetArgumentPossibilities<'src> for UnionType<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - let mut result = Vec::new(); - for ty in &self.body.list { - result.extend(ty.get_argument_possibilities(record)?.into_iter()); - } - Some(result) - } -} - -impl<'src> GetArgumentPossibilities<'src> for Type<'src> { - fn get_argument_possibilities(&self, record: &FirstPassRecord<'src>) -> Option> { - match self { - Type::Single(single) => single.get_argument_possibilities(record), - Type::Union(union) => union.get_argument_possibilities(record), - } - } -} - -/// Implemented on an AST type node to generate a snake case name. -trait GetTypeName { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String); - - fn get_type_name(&self, record: &FirstPassRecord) -> String { - let mut string = String::new(); - self.push_type_name(record, &mut string); - return string; - } -} - -impl GetTypeName for [T] { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - let mut first = true; - for union_member_type in self { - if first { - first = false; - } else { - dst.push_str("_and_"); - } - union_member_type.push_type_name(record, dst); - } - } -} - -impl GetTypeName for MayBeNull { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - if self.q_mark.is_some() { - dst.push_str("opt_"); - } - self.type_.push_type_name(record, dst); - } -} - -macro_rules! term_type_names { - ($($t:tt => $r:tt)*) => ($( - impl GetTypeName for weedle::term::$t { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - dst.push_str($r); - } - } - )*) -} - -term_type_names!( - Boolean => "bool" - Byte => "i8" - Octet => "u8" - Int8Array => "i8_array" - Uint8Array => "u8_array" - Uint8ClampedArray => "u8_clamped_array" - Int16Array => "i16_array" - Uint16Array => "u16_array" - Int32Array => "i32_array" - Uint32Array => "u32_array" - Float32Array => "f32_array" - Float64Array => "f64_array" - USVString => "usv_str" - ByteString => "byte_str" - DOMString => "dom_str" - ArrayBuffer => "array_buffer" - Symbol => "symbol" - Object => "object" - DataView => "data_view" - Error => "error" -); - -impl<'src> GetTypeName for weedle::types::ReturnType<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match self { - weedle::types::ReturnType::Type(ty) => (*ty).push_type_name(record, dst), - weedle::types::ReturnType::Void(_) => dst.push_str("void"), - } - } -} - -impl GetTypeName for weedle::types::StringType { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - match self { - weedle::types::StringType::Byte(_) => dst.push_str("byte_str"), - weedle::types::StringType::DOM(_) => dst.push_str("dom_str"), - weedle::types::StringType::USV(_) => dst.push_str("usv_str"), - } - } -} - -impl<'src> GetTypeName for weedle::common::Identifier<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match record.typedefs.get(self.0) { - Some(other) => other.push_type_name(record, dst), - None => dst.push_str(&self.0.to_snake_case()), - } - } -} - -impl GetTypeName for IntegerType { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - match self { - IntegerType::LongLong(l) if l.unsigned.is_some() => dst.push_str("u64"), - IntegerType::LongLong(_) => dst.push_str("i64"), - IntegerType::Long(l) if l.unsigned.is_some() => dst.push_str("u32"), - IntegerType::Long(_) => dst.push_str("i32"), - IntegerType::Short(l) if l.unsigned.is_some() => dst.push_str("u16"), - IntegerType::Short(_) => dst.push_str("i16"), - } - } -} - -impl GetTypeName for FloatingPointType { - fn push_type_name(&self, _record: &FirstPassRecord, dst: &mut String) { - match self { - FloatingPointType::Float(_) => dst.push_str("f32"), - FloatingPointType::Double(_) => dst.push_str("f64"), - } - } -} - -impl<'src> GetTypeName for SequenceType<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - self.generics.body.push_type_name(record, dst); - dst.push_str("_seq"); - } -} - -impl<'src> GetTypeName for PromiseType<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - self.generics.body.push_type_name(record, dst); - dst.push_str("_promise"); - } -} - -impl<'src> GetTypeName for FrozenArrayType<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - self.generics.body.push_type_name(record, dst); - dst.push_str("_frozen_array"); - } -} - -impl<'src> GetTypeName for RecordType<'src> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - dst.push_str("record_from_"); - self.generics.body.0.push_type_name(record, dst); - dst.push_str("_to_"); - self.generics.body.2.push_type_name(record, dst); - } -} - -impl<'a> GetTypeName for NonAnyType<'a> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match self { - NonAnyType::Boolean(s) => s.push_type_name(record, dst), - NonAnyType::Octet(s) => s.push_type_name(record, dst), - NonAnyType::Byte(s) => s.push_type_name(record, dst), - NonAnyType::Identifier(s) => s.push_type_name(record, dst), - NonAnyType::Integer(s) => s.push_type_name(record, dst), - NonAnyType::FloatingPoint(s) => s.push_type_name(record, dst), - - NonAnyType::Float32Array(s) => s.push_type_name(record, dst), - NonAnyType::Float64Array(s) => s.push_type_name(record, dst), - NonAnyType::Int8Array(s) => s.push_type_name(record, dst), - NonAnyType::Int16Array(s) => s.push_type_name(record, dst), - NonAnyType::Int32Array(s) => s.push_type_name(record, dst), - NonAnyType::Uint8Array(s) => s.push_type_name(record, dst), - NonAnyType::Uint8ClampedArray(s) => s.push_type_name(record, dst), - NonAnyType::Uint16Array(s) => s.push_type_name(record, dst), - NonAnyType::Uint32Array(s) => s.push_type_name(record, dst), - - NonAnyType::DOMString(s) => s.push_type_name(record, dst), - NonAnyType::ByteString(s) => s.push_type_name(record, dst), - NonAnyType::USVString(s) => s.push_type_name(record, dst), - NonAnyType::ArrayBuffer(s) => s.push_type_name(record, dst), - - NonAnyType::DataView(s) => s.push_type_name(record, dst), - NonAnyType::Error(s) => s.push_type_name(record, dst), - NonAnyType::FrozenArrayType(s) => s.push_type_name(record, dst), - NonAnyType::Object(s) => s.push_type_name(record, dst), - NonAnyType::Promise(s) => s.push_type_name(record, dst), - NonAnyType::RecordType(s) => s.push_type_name(record, dst), - NonAnyType::Sequence(s) => s.push_type_name(record, dst), - NonAnyType::Symbol(s) => s.push_type_name(record, dst), - } - } -} - -impl<'a> GetTypeName for SingleType<'a> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match self { - SingleType::Any(_) => dst.push_str("any"), - SingleType::NonAny(non_any) => non_any.push_type_name(record, dst), - } - } -} - -impl<'a> GetTypeName for UnionMemberType<'a> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match self { - UnionMemberType::Single(single) => single.push_type_name(record, dst), - UnionMemberType::Union(union) => union.push_type_name(record, dst), - } - } -} - -impl<'a> GetTypeName for UnionType<'a> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - dst.push_str("union_of_"); - self.body.list.push_type_name(record, dst); - } -} - -impl<'a> GetTypeName for Type<'a> { - fn push_type_name(&self, record: &FirstPassRecord, dst: &mut String) { - match self { - Type::Single(single) => single.push_type_name(record, dst), - Type::Union(union) => union.push_type_name(record, dst), - } - } -} - impl<'src> FirstPassRecord<'src> { - /// Uses the first pass to convert webidl function arguments to all possible argument combinations. - /// Returns option that contains a value if the conversion succeeds. - /// The value is a vector of argument possibilities. - /// Each possibility is a vector of tuples of converted argument (syn) types and type names - fn get_arguments_possibilities(&self, arguments: &'src [Argument]) -> Option>> { - fn get_argument_possibilities(record: &FirstPassRecord, argument: &Argument) -> Option> { - let type_ = match argument { - Argument::Single(single) => &single.type_.type_, - Argument::Variadic(variadic) => &variadic.type_, - }; - match type_.get_argument_possibilities(record) { - None => { - warn!("Argument's type is not yet supported: {:?}", argument); - None - }, - Some(value) => Some(value), - } - } - if !arguments.is_empty() { - let mut optional_arguments_possibilities = Vec::new(); - if let Argument::Single(ref single) = arguments[0] { - if single.optional.is_some() { - optional_arguments_possibilities.push(Vec::new()); - } - } - let mut arguments_possibilities: Vec<_> = get_argument_possibilities( - self, - &arguments[0] - )? - .into_iter() - .map(|argument_possibility| vec![argument_possibility]) - .collect(); - for argument in arguments[1..].iter() { - let mut new_arguments_possibilities = Vec::new(); - for arguments_possibility in arguments_possibilities { - if let Argument::Single(single) = argument { - if single.optional.is_some() { - optional_arguments_possibilities.push(arguments_possibility.clone()); - } - } - let mut element_argument_possibilities = get_argument_possibilities( - self, - &argument - )?; - for element_argument_possibility in element_argument_possibilities { - new_arguments_possibilities.push( - arguments_possibility - .iter() - .cloned() - .chain(iter::once(element_argument_possibility)) - .collect() - ) - } - } - arguments_possibilities = new_arguments_possibilities - } - optional_arguments_possibilities.extend(arguments_possibilities.into_iter()); - Some(optional_arguments_possibilities) - } else { - Some(vec![Vec::new()]) - } - } - - /// Uses the first pass to convert webidl function arguments to rust arguments. - /// - /// `kind` is whether the function is a method, in which case we would need a `self` - /// parameter. - /// - /// Returns option that contains a value if the conversion succeeds. - /// The value is a vector of argument variants. - /// Each variant is a vector of tuples of original arguments, converted argument types and type names. - fn get_variants( - &self, - arguments: &'src [Argument], - kind: &backend::ast::ImportFunctionKind, - ) -> Option, syn::ArgCaptured, Option)>>> - { - let arguments_possibilities = self.get_arguments_possibilities(arguments)?; - let mut result = Vec::new(); - for arguments_possibility in arguments_possibilities { - let mut res = if let backend::ast::ImportFunctionKind::Method { - ty, - kind: backend::ast::MethodKind::Operation( - backend::ast::Operation { - is_static: false, .. - } - ), - .. - } = kind { - let mut res = Vec::with_capacity(arguments.len() + 1); - res.push( - ( - None, - simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone())), - None, - ), - ); - res - } else { - Vec::with_capacity(arguments.len()) - }; - for (argument, argument_possibility) in arguments.iter().zip(arguments_possibility) { - let single = match argument { - Argument::Single(single) => single, - Argument::Variadic(_) => return None, - }; - res.push( - ( - Some(argument), - simple_fn_arg( - rust_ident(&single.identifier.0.to_snake_case()), - argument_possibility.0.clone() - ), - Some(argument_possibility.1.clone()), - ), - ); - } - result.push(res); - } - - Some(result) - } - /// Create a wasm-bindgen function, if possible. pub fn create_function( &self, name: &str, overloaded: bool, same_argument_names: bool, - arguments: &[Argument], - mut ret: Option, + arguments: &[(&str, IdlType<'src>, bool)], + ret: IdlType<'src>, kind: backend::ast::ImportFunctionKind, structural: bool, catch: bool, doc_comment: Option, - ) -> Option> - { + ) -> Vec { let rust_name = if overloaded && !arguments.is_empty() { let mut argument_type_names = String::new(); for argument in arguments { - let argument = match argument { - Argument::Single(single) => single, - Argument::Variadic(_) => return None, - }; if argument_type_names.len() > 0 { argument_type_names.push_str("_and_"); } if same_argument_names { - argument.type_.type_.push_type_name(self, &mut argument_type_names); + argument.1.push_type_name(&mut argument_type_names); } else { - argument_type_names.push_str(&argument.identifier.0.to_snake_case()); + argument_type_names.push_str(&argument.0.to_snake_case()); } } if name == "new" { @@ -1006,46 +230,53 @@ impl<'src> FirstPassRecord<'src> { name.to_snake_case() }; + let ret = match ret { + IdlType::Void => None, + ret @ _ => { + match ret.to_syn_type(TypePosition::Return) { + None => return Vec::new(), + Some(ret) => Some(ret), + } + }, + }; + let js_ret = ret.clone(); - if catch { - ret = Some(ret.map_or_else(|| result_ty(unit_ty()), result_ty)) - } + let ret = if catch { + Some(ret.map_or_else(|| result_ty(unit_ty()), result_ty)) + } else { + ret + }; - let variants = self.get_variants(arguments, &kind)?; - let mut arguments_count_variants_count = BTreeMap::new(); - for variant in &variants { - arguments_count_variants_count - .entry(variant.len()) - .and_modify(|variants_count| { *variants_count += 1usize; }) - .or_insert(1usize); + let converted_arguments = arguments + .iter() + .cloned() + .map(|(_name, idl_type, optional)| (idl_type, optional)) + .collect::>(); + let possibilities = flatten(&converted_arguments); + let mut arguments_count_multiple = BTreeMap::new(); + for idl_types in &possibilities { + arguments_count_multiple + .entry(idl_types.len()) + .and_modify(|variants_count| { *variants_count = true; }) + .or_insert(false); } - let mut result = Vec::new(); - for variant in &variants { - let rust_name = if variants.len() > 1 { + let mut import_functions = Vec::new(); + 'outer: for idl_types in &possibilities { + let rust_name = if possibilities.len() > 1 { let mut rust_name = rust_name.clone(); let mut first = true; - for (argument, _, variant_name) in variant { - if let Some(type_name) = variant_name { - if first { - rust_name.push_str("_using_"); - first = false; - } else { - rust_name.push_str("_and_"); - } - if arguments_count_variants_count[&variant.len()] == 1 { - if let Some(argument) = argument { - let argument = match argument { - Argument::Single(single) => single, - Argument::Variadic(_) => return None, - }; - rust_name.push_str(&argument.identifier.0.to_snake_case()); - } else { - rust_name.push_str(&type_name); - } - } else { - rust_name.push_str(&type_name); - } + for ((argument_name, _, _), idl_type) in arguments.iter().zip(idl_types) { + if first { + rust_name.push_str("_using_"); + first = false; + } else { + rust_name.push_str("_and_"); + } + if arguments_count_multiple[&idl_types.len()] { + idl_type.push_type_name(&mut rust_name); + } else { + rust_name.push_str(&argument_name.to_snake_case()); } } rust_name @@ -1062,10 +293,35 @@ impl<'src> FirstPassRecord<'src> { raw_ident(&format!("__widl_f_{}_{}", rust_name, ns)) }; - result.push(backend::ast::ImportFunction { + let mut args_captured = if let &backend::ast::ImportFunctionKind::Method { + ref ty, + kind: backend::ast::MethodKind::Operation( + backend::ast::Operation { + is_static: false, .. + } + ), + .. + } = &kind { + let mut res = Vec::with_capacity(idl_types.len() + 1); + res.push(simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone()))); + res + } else { + Vec::with_capacity(idl_types.len()) + }; + for ((argument_name, _, _), idl_type) in arguments.iter().zip(idl_types) { + let syn_type = if let Some(syn_type) = idl_type.to_syn_type(TypePosition::Argument) { + syn_type + } else { + continue 'outer; + }; + let argument_name = rust_ident(&argument_name.to_snake_case()); + args_captured.push(simple_fn_arg(argument_name, syn_type)); + } + + import_functions.push(backend::ast::ImportFunction { function: backend::ast::Function { name: name.to_string(), - arguments: variant.iter().map(|variant| variant.1.clone()).collect(), + arguments: args_captured, ret: ret.clone(), rust_attrs: vec![], rust_vis: public(), @@ -1079,7 +335,32 @@ impl<'src> FirstPassRecord<'src> { doc_comment: doc_comment.clone(), }) } - Some(result) + import_functions + } + + /// Convert arguments to ones suitable crating function + pub(crate) fn convert_arguments( + &self, + arguments: &[weedle::argument::Argument<'src>], + ) -> Option, bool)>> { + let mut converted_arguments = Vec::with_capacity(arguments.len()); + for argument in arguments { + let name = match argument { + Argument::Single(single) => single.identifier.0, + Argument::Variadic(variadic) => variadic.identifier.0, + }; + let ty = match argument { + Argument::Single(single) => &single.type_.type_, + Argument::Variadic(variadic) => &variadic.type_, + }; + let idl_type = ty.to_idl_type(self)?; + let optional = match argument { + Argument::Single(single) => single.optional.is_some(), + Argument::Variadic(_variadic) => false, + }; + converted_arguments.push((name, idl_type, optional)); + } + Some(converted_arguments) } /// Create a wasm-bindgen method, if possible. @@ -1092,7 +373,7 @@ impl<'src> FirstPassRecord<'src> { is_static: bool, structural: bool, catch: bool, - ) -> Option> { + ) -> Vec { let (overloaded, same_argument_names) = self.get_operation_overloading( arguments, &operation_id, @@ -1104,7 +385,7 @@ impl<'src> FirstPassRecord<'src> { ::first_pass::OperationId::Operation(name) => match name { None => { warn!("Operations without a name are unsupported"); - return None; + return Vec::new(); } Some(name) => name.to_string(), }, @@ -1128,18 +409,11 @@ impl<'src> FirstPassRecord<'src> { }), }; - let ret = match return_type { - weedle::types::ReturnType::Void(_) => None, - weedle::types::ReturnType::Type(ty) => { - match ty.to_syn_type(self, TypePosition::Return) { - None => { - warn!("Operation's return type is not yet supported: {:?}", ty); - return None; - } - Some(ty) => Some(ty), - } - } + let ret = match return_type.to_idl_type(self) { + None => return Vec::new(), + Some(idl_type) => idl_type, }; + let doc_comment = match &operation_id { ::first_pass::OperationId::Constructor => panic!("constructors are unsupported"), ::first_pass::OperationId::Operation(_) => Some( @@ -1154,11 +428,16 @@ impl<'src> FirstPassRecord<'src> { ::first_pass::OperationId::IndexingDeleter => Some("The indexing deleter\n\n".to_string()), }; + let arguments = match self.convert_arguments(arguments) { + None => return Vec::new(), + Some(arguments) => arguments + }; + self.create_function( &name, overloaded, same_argument_names, - arguments, + &arguments, ret, kind, structural, @@ -1230,13 +509,10 @@ impl<'src> FirstPassRecord<'src> { is_static: bool, is_structural: bool, catch: bool, - ) -> Option> { - let ret = match ty.to_syn_type(self, TypePosition::Return) { - None => { - warn!("Attribute's type does not yet support reading: {:?}", ty); - return None; - } - Some(ty) => Some(ty), + ) -> Vec { + let ret = match ty.to_idl_type(self) { + None => return Vec::new(), + Some(idl_type) => idl_type, }; let kind = backend::ast::ImportFunctionKind::Method { @@ -1261,7 +537,7 @@ impl<'src> FirstPassRecord<'src> { is_static: bool, is_structural: bool, catch: bool, - ) -> Option> { + ) -> Vec { let kind = backend::ast::ImportFunctionKind::Method { class: self_name.to_string(), ty: ident_ty(rust_ident(camel_case_ident(&self_name).as_str())), @@ -1276,17 +552,15 @@ impl<'src> FirstPassRecord<'src> { &format!("set_{}", name), false, false, - &[Argument::Single(SingleArgument { - attributes: None, - optional: None, - type_: AttributedType { - attributes: None, - type_: ty, + &[( + name, + match ty.to_idl_type(self) { + None => return Vec::new(), + Some(idl_type) => idl_type, }, - identifier: Identifier(name), - default: None, - })], - None, + false, + )], + IdlType::Void, kind, is_structural, catch,