From ff516d0211f3592923e0137bc4e500d00447a212 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Sun, 2 Sep 2018 15:09:51 +0300 Subject: [PATCH 1/6] Add initial support for unions in return types, add more fixes for case of identifiers --- crates/webidl/src/idl_type.rs | 140 ++++++++++++++++++++++++++-------- crates/webidl/src/lib.rs | 9 +-- crates/webidl/src/util.rs | 33 ++++++-- 3 files changed, 138 insertions(+), 44 deletions(-) diff --git a/crates/webidl/src/idl_type.rs b/crates/webidl/src/idl_type.rs index 4942df4e..30a80316 100644 --- a/crates/webidl/src/idl_type.rs +++ b/crates/webidl/src/idl_type.rs @@ -1,12 +1,11 @@ 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}; +use util::{TypePosition, camel_case_ident, snake_case_ident, shared_ref, option_ty, array}; #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] pub(crate) enum IdlType<'a> { @@ -336,8 +335,88 @@ terms_to_idl_type! { } impl<'a> IdlType<'a> { + /// Generates a camel case type name. + #[allow(dead_code)] + pub(crate) fn push_camel_case_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 + | IdlType::UnrestrictedFloat => dst.push_str("F32"), + | IdlType::Double + | IdlType::UnrestrictedDouble => dst.push_str("F64"), + | IdlType::DomString + | IdlType::ByteString + | IdlType::UsvString => dst.push_str("Str"), + IdlType::Object => dst.push_str("Object"), + IdlType::Symbol => dst.push_str("Symbol"), + IdlType::Error => dst.push_str("Error"), + + IdlType::ArrayBuffer => dst.push_str("ArrayBuffer"), + IdlType::DataView => dst.push_str("DataView"), + IdlType::Int8Array => dst.push_str("I8Array"), + IdlType::Uint8Array => dst.push_str("U8Array"), + IdlType::Uint8ClampedArray => dst.push_str("U8ClampedArray"), + IdlType::Int16Array => dst.push_str("I16Array"), + IdlType::Uint16Array => dst.push_str("U16Array"), + IdlType::Int32Array => dst.push_str("I32Array"), + IdlType::Uint32Array => dst.push_str("U32Array"), + IdlType::Float32Array => dst.push_str("F32Array"), + IdlType::Float64Array => dst.push_str("F64Array"), + + IdlType::Interface(name) => dst.push_str(&camel_case_ident(name)), + IdlType::Dictionary(name) => dst.push_str(&camel_case_ident(name)), + IdlType::Enum(name) => dst.push_str(&camel_case_ident(name)), + + IdlType::Nullable(idl_type) => { + dst.push_str("Opt"); + idl_type.push_snake_case_name(dst); + }, + IdlType::FrozenArray(idl_type) => { + idl_type.push_snake_case_name(dst); + dst.push_str("FrozenArray"); + }, + IdlType::Sequence(idl_type) => { + idl_type.push_snake_case_name(dst); + dst.push_str("Sequence"); + }, + IdlType::Promise(idl_type) => { + idl_type.push_snake_case_name(dst); + dst.push_str("Promise"); + }, + IdlType::Record(idl_type_from, idl_type_to) => { + dst.push_str("RecordFrom"); + idl_type_from.push_snake_case_name(dst); + dst.push_str("To"); + idl_type_to.push_snake_case_name(dst); + }, + IdlType::Union(idl_types) => { + dst.push_str("UnionOf"); + let mut first = true; + for idl_type in idl_types { + if first { + first = false; + } else { + dst.push_str("And"); + } + idl_type.push_snake_case_name(dst); + } + }, + + IdlType::Any => dst.push_str("Any"), + IdlType::Void => dst.push_str("Void"), + } + } + /// Generates a snake case type name. - pub(crate) fn push_type_name(&self, dst: &mut String) { + pub(crate) fn push_snake_case_name(&self, dst: &mut String) { match self { IdlType::Boolean => dst.push_str("bool"), IdlType::Byte => dst.push_str("i8"), @@ -348,13 +427,13 @@ impl<'a> IdlType<'a> { IdlType::UnsignedLong => dst.push_str("u32"), IdlType::LongLong => dst.push_str("i64"), IdlType::UnsignedLongLong => dst.push_str("u64"), - IdlType::Float | - IdlType::UnrestrictedFloat => dst.push_str("f32"), - IdlType::Double | - IdlType::UnrestrictedDouble => dst.push_str("f64"), - IdlType::DomString | - IdlType::ByteString | - IdlType::UsvString => dst.push_str("str"), + | IdlType::Float + | IdlType::UnrestrictedFloat => dst.push_str("f32"), + | IdlType::Double + | IdlType::UnrestrictedDouble => dst.push_str("f64"), + | IdlType::DomString + | IdlType::ByteString + | IdlType::UsvString => dst.push_str("str"), IdlType::Object => dst.push_str("object"), IdlType::Symbol => dst.push_str("symbol"), IdlType::Error => dst.push_str("error"), @@ -371,31 +450,31 @@ impl<'a> IdlType<'a> { 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::Interface(name) => dst.push_str(&snake_case_ident(name)), + IdlType::Dictionary(name) => dst.push_str(&snake_case_ident(name)), + IdlType::Enum(name) => dst.push_str(&snake_case_ident(name)), IdlType::Nullable(idl_type) => { dst.push_str("opt_"); - idl_type.push_type_name(dst); + idl_type.push_snake_case_name(dst); }, IdlType::FrozenArray(idl_type) => { - idl_type.push_type_name(dst); + idl_type.push_snake_case_name(dst); dst.push_str("_frozen_array"); }, IdlType::Sequence(idl_type) => { - idl_type.push_type_name(dst); + idl_type.push_snake_case_name(dst); dst.push_str("_sequence"); }, IdlType::Promise(idl_type) => { - idl_type.push_type_name(dst); + idl_type.push_snake_case_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); + idl_type_from.push_snake_case_name(dst); dst.push_str("_to_"); - idl_type_to.push_type_name(dst); + idl_type_to.push_snake_case_name(dst); }, IdlType::Union(idl_types) => { dst.push_str("union_of_"); @@ -406,7 +485,7 @@ impl<'a> IdlType<'a> { } else { dst.push_str("_and_"); } - idl_type.push_type_name(dst); + idl_type.push_snake_case_name(dst); } }, @@ -415,14 +494,6 @@ impl<'a> IdlType<'a> { } } - /// 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 { @@ -467,8 +538,8 @@ impl<'a> IdlType<'a> { IdlType::Float32Array => Some(array("f32", pos)), IdlType::Float64Array => Some(array("f64", pos)), - IdlType::Interface(name) | - IdlType::Dictionary(name) => { + | IdlType::Interface(name) + | IdlType::Dictionary(name) => { let ty = ident_ty(rust_ident(camel_case_ident(name).as_str())); if pos == TypePosition::Argument { Some(shared_ref(ty)) @@ -491,7 +562,12 @@ impl<'a> IdlType<'a> { } } IdlType::Record(_idl_type_from, _idl_type_to) => None, - IdlType::Union(_idl_types) => None, + IdlType::Union(_idl_types) => { + // Handles union types in all places except operation argument types + // TODO: add better support for union types here? + let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; + Some(leading_colon_path_ty(path)) + }, IdlType::Any => { let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; @@ -552,7 +628,7 @@ impl<'a> IdlType<'a> { .flat_map(|idl_type| idl_type.flatten()) .collect(), - idl_type @ _ => vec![idl_type.clone()] + idl_type @ _ => vec![idl_type.clone()], } } } diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index aed5c584..fef0f47d 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -41,14 +41,13 @@ use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports}; use backend::defined::ImportedTypeReferences; use backend::util::{ident_ty, rust_ident, raw_ident, wrap_import_function}; use failure::ResultExt; -use heck::{ShoutySnakeCase, SnakeCase}; use proc_macro2::{Ident, Span}; use weedle::attribute::{ExtendedAttributeList}; use weedle::dictionary::DictionaryMember; use first_pass::{FirstPass, FirstPassRecord, OperationId, InterfaceData}; use first_pass::OperationData; -use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc}; +use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, shouty_snake_case_ident, snake_case_ident, mdn_doc}; use idl_type::ToIdlType; pub use error::{Error, ErrorKind, Result}; @@ -280,7 +279,7 @@ impl<'src> FirstPassRecord<'src> { Some(ast::DictionaryField { required: field.required.is_some(), - name: rust_ident(&field.identifier.0.to_snake_case()), + name: rust_ident(&snake_case_ident(field.identifier.0)), ty, }) } @@ -293,7 +292,7 @@ impl<'src> FirstPassRecord<'src> { ) { let mut module = backend::ast::Module { vis: public(), - name: rust_ident(name.to_snake_case().as_str()), + name: rust_ident(snake_case_ident(name).as_str()), imports: Default::default(), }; @@ -368,7 +367,7 @@ impl<'src> FirstPassRecord<'src> { program.consts.push(backend::ast::Const { vis: public(), - name: rust_ident(member.identifier.0.to_shouty_snake_case().as_str()), + name: rust_ident(shouty_snake_case_ident(member.identifier.0).as_str()), class: Some(rust_ident(camel_case_ident(&self_name).as_str())), ty, value: webidl_const_v_to_backend_const_v(&member.const_value), diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index b91478f9..8bdc5942 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -3,7 +3,7 @@ use std::ptr; use backend; use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident}; -use heck::{CamelCase, SnakeCase}; +use heck::{CamelCase, ShoutySnakeCase, SnakeCase}; use proc_macro2::Ident; use syn; use weedle; @@ -23,9 +23,28 @@ pub(crate) fn shared_ref(ty: syn::Type) -> syn::Type { }.into() } -/// Fix camelcase of identifiers like HTMLBRElement +/// Fix case of identifiers like `HTMLBRElement` or `texImage2D` +fn fix_ident(identifier: &str) -> String { + identifier + .replace("HTML", "HTML_") + .replace("1D", "_1d") + .replace("2D", "_2d") + .replace("3D", "_3d") +} + +/// Convert an identifier to camel case pub fn camel_case_ident(identifier: &str) -> String { - identifier.replace("HTML", "HTML_").to_camel_case() + fix_ident(identifier).to_camel_case() +} + +/// Convert an identifier to shouty snake case +pub fn shouty_snake_case_ident(identifier: &str) -> String { + fix_ident(identifier).to_shouty_snake_case() +} + +/// Convert an identifier to snake case +pub fn snake_case_ident(identifier: &str) -> String { + fix_ident(identifier).to_snake_case() } // Returns a link to MDN @@ -308,7 +327,7 @@ impl<'src> FirstPassRecord<'src> { let ret = ty.to_idl_type(self)?; self.create_one_function( &name, - &name.to_snake_case(), + &snake_case_ident(name), None.into_iter(), &ret, kind, @@ -492,7 +511,7 @@ impl<'src> FirstPassRecord<'src> { None => continue, }; - let mut rust_name = name.to_snake_case(); + let mut rust_name = snake_case_ident(name); let mut first = true; for (i, arg) in signature.args.iter().enumerate() { // Find out if any other known signature either has the same @@ -539,9 +558,9 @@ impl<'src> FirstPassRecord<'src> { // then we can't use that if the types are also the same because // otherwise it could be ambiguous. if any_same_name && any_different_type { - arg.push_type_name(&mut rust_name); + arg.push_snake_case_name(&mut rust_name); } else { - rust_name.push_str(&arg_name.to_snake_case()); + rust_name.push_str(&snake_case_ident(arg_name)); } } ret.extend(self.create_one_function( From 75ac7ca64e9fcd13d1793c8e9d9b12e48545ba68 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Sun, 2 Sep 2018 20:37:12 +0300 Subject: [PATCH 2/6] Fix nullable union types --- crates/webidl/src/idl_type.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/webidl/src/idl_type.rs b/crates/webidl/src/idl_type.rs index 30a80316..c065576e 100644 --- a/crates/webidl/src/idl_type.rs +++ b/crates/webidl/src/idl_type.rs @@ -549,7 +549,12 @@ impl<'a> IdlType<'a> { }, 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::Nullable(idl_type) => { + match **idl_type { + IdlType::Union(..) => idl_type.to_syn_type(pos), + _ => Some(option_ty(idl_type.to_syn_type(pos)?)) + } + }, IdlType::FrozenArray(_idl_type) => None, IdlType::Sequence(_idl_type) => None, IdlType::Promise(_idl_type) => { @@ -563,10 +568,10 @@ impl<'a> IdlType<'a> { } IdlType::Record(_idl_type_from, _idl_type_to) => None, IdlType::Union(_idl_types) => { - // Handles union types in all places except operation argument types + // Handles union types in all places except operation argument types. + // Currently treats them as any type. // TODO: add better support for union types here? - let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; - Some(leading_colon_path_ty(path)) + IdlType::Any.to_syn_type(pos) }, IdlType::Any => { From 14c2c04ccd896a71db074a7960933aaa4f2fcf67 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Sun, 2 Sep 2018 20:51:37 +0300 Subject: [PATCH 3/6] Fix canvas test --- examples/canvas/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/canvas/src/lib.rs b/examples/canvas/src/lib.rs index 420016e2..1b200c4b 100755 --- a/examples/canvas/src/lib.rs +++ b/examples/canvas/src/lib.rs @@ -19,7 +19,7 @@ pub fn draw() { .get_context("2d") .unwrap() .unwrap() - .dyn_into::() + .dyn_into::() .unwrap(); context.begin_path(); From 095f86fa51ef42b6c167b6f6b925b257dcee7922 Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Mon, 3 Sep 2018 21:37:58 +0300 Subject: [PATCH 4/6] Use object type whenever possible --- crates/webidl/src/idl_type.rs | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/crates/webidl/src/idl_type.rs b/crates/webidl/src/idl_type.rs index c065576e..694ac7f3 100644 --- a/crates/webidl/src/idl_type.rs +++ b/crates/webidl/src/idl_type.rs @@ -551,7 +551,19 @@ impl<'a> IdlType<'a> { IdlType::Nullable(idl_type) => { match **idl_type { - IdlType::Union(..) => idl_type.to_syn_type(pos), + IdlType::Union(ref idl_types) => + if idl_types + .iter() + .all(|idl_type| + match idl_type { + IdlType::Interface(..) => true, + _ => false, + } + ) { + IdlType::Nullable(Box::new(IdlType::Object)).to_syn_type(pos) + } else { + IdlType::Any.to_syn_type(pos) + }, _ => Some(option_ty(idl_type.to_syn_type(pos)?)) } }, @@ -567,11 +579,22 @@ impl<'a> IdlType<'a> { } } IdlType::Record(_idl_type_from, _idl_type_to) => None, - IdlType::Union(_idl_types) => { + IdlType::Union(idl_types) => { // Handles union types in all places except operation argument types. - // Currently treats them as any type. + // Currently treats them as object or any types. // TODO: add better support for union types here? - IdlType::Any.to_syn_type(pos) + if idl_types + .iter() + .all(|idl_type| + match idl_type { + IdlType::Interface(..) => true, + _ => false, + } + ) { + IdlType::Object.to_syn_type(pos) + } else { + IdlType::Any.to_syn_type(pos) + } }, IdlType::Any => { From fcd890a70e88421b2a02e9dbbe31548ba5fc137c Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Tue, 4 Sep 2018 21:03:23 +0300 Subject: [PATCH 5/6] Remove dead code --- crates/webidl/src/idl_type.rs | 80 ----------------------------------- 1 file changed, 80 deletions(-) diff --git a/crates/webidl/src/idl_type.rs b/crates/webidl/src/idl_type.rs index 694ac7f3..63f952d6 100644 --- a/crates/webidl/src/idl_type.rs +++ b/crates/webidl/src/idl_type.rs @@ -335,86 +335,6 @@ terms_to_idl_type! { } impl<'a> IdlType<'a> { - /// Generates a camel case type name. - #[allow(dead_code)] - pub(crate) fn push_camel_case_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 - | IdlType::UnrestrictedFloat => dst.push_str("F32"), - | IdlType::Double - | IdlType::UnrestrictedDouble => dst.push_str("F64"), - | IdlType::DomString - | IdlType::ByteString - | IdlType::UsvString => dst.push_str("Str"), - IdlType::Object => dst.push_str("Object"), - IdlType::Symbol => dst.push_str("Symbol"), - IdlType::Error => dst.push_str("Error"), - - IdlType::ArrayBuffer => dst.push_str("ArrayBuffer"), - IdlType::DataView => dst.push_str("DataView"), - IdlType::Int8Array => dst.push_str("I8Array"), - IdlType::Uint8Array => dst.push_str("U8Array"), - IdlType::Uint8ClampedArray => dst.push_str("U8ClampedArray"), - IdlType::Int16Array => dst.push_str("I16Array"), - IdlType::Uint16Array => dst.push_str("U16Array"), - IdlType::Int32Array => dst.push_str("I32Array"), - IdlType::Uint32Array => dst.push_str("U32Array"), - IdlType::Float32Array => dst.push_str("F32Array"), - IdlType::Float64Array => dst.push_str("F64Array"), - - IdlType::Interface(name) => dst.push_str(&camel_case_ident(name)), - IdlType::Dictionary(name) => dst.push_str(&camel_case_ident(name)), - IdlType::Enum(name) => dst.push_str(&camel_case_ident(name)), - - IdlType::Nullable(idl_type) => { - dst.push_str("Opt"); - idl_type.push_snake_case_name(dst); - }, - IdlType::FrozenArray(idl_type) => { - idl_type.push_snake_case_name(dst); - dst.push_str("FrozenArray"); - }, - IdlType::Sequence(idl_type) => { - idl_type.push_snake_case_name(dst); - dst.push_str("Sequence"); - }, - IdlType::Promise(idl_type) => { - idl_type.push_snake_case_name(dst); - dst.push_str("Promise"); - }, - IdlType::Record(idl_type_from, idl_type_to) => { - dst.push_str("RecordFrom"); - idl_type_from.push_snake_case_name(dst); - dst.push_str("To"); - idl_type_to.push_snake_case_name(dst); - }, - IdlType::Union(idl_types) => { - dst.push_str("UnionOf"); - let mut first = true; - for idl_type in idl_types { - if first { - first = false; - } else { - dst.push_str("And"); - } - idl_type.push_snake_case_name(dst); - } - }, - - IdlType::Any => dst.push_str("Any"), - IdlType::Void => dst.push_str("Void"), - } - } - /// Generates a snake case type name. pub(crate) fn push_snake_case_name(&self, dst: &mut String) { match self { From e5f382eccfd3bbdddf94231d60cccdb67c20d8cb Mon Sep 17 00:00:00 2001 From: Anton Danilkin Date: Wed, 5 Sep 2018 18:15:02 +0300 Subject: [PATCH 6/6] Remove support for non-object unions, add more comments --- crates/webidl/src/idl_type.rs | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/crates/webidl/src/idl_type.rs b/crates/webidl/src/idl_type.rs index 63f952d6..8a681900 100644 --- a/crates/webidl/src/idl_type.rs +++ b/crates/webidl/src/idl_type.rs @@ -469,24 +469,7 @@ impl<'a> IdlType<'a> { }, IdlType::Enum(name) => Some(ident_ty(rust_ident(camel_case_ident(name).as_str()))), - IdlType::Nullable(idl_type) => { - match **idl_type { - IdlType::Union(ref idl_types) => - if idl_types - .iter() - .all(|idl_type| - match idl_type { - IdlType::Interface(..) => true, - _ => false, - } - ) { - IdlType::Nullable(Box::new(IdlType::Object)).to_syn_type(pos) - } else { - IdlType::Any.to_syn_type(pos) - }, - _ => Some(option_ty(idl_type.to_syn_type(pos)?)) - } - }, + 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) => { @@ -501,8 +484,12 @@ impl<'a> IdlType<'a> { IdlType::Record(_idl_type_from, _idl_type_to) => None, IdlType::Union(idl_types) => { // Handles union types in all places except operation argument types. - // Currently treats them as object or any types. + // Currently treats them as object type, if possible. // TODO: add better support for union types here? + // Approaches for it: + // 1. Use strategy of finding the nearest common subclass (finding the best type + // that is suitable for all values of this union) + // 2. Generate enum with payload in Rust for each union type if idl_types .iter() .all(|idl_type| @@ -513,7 +500,7 @@ impl<'a> IdlType<'a> { ) { IdlType::Object.to_syn_type(pos) } else { - IdlType::Any.to_syn_type(pos) + None } },